main.c 12.6 KB
Newer Older
1
/*
2
 * Uninstaller
3
 *
4 5 6
 * Copyright 2000 Andreas Mohr
 * Copyright 2004 Hannu Valtonen
 * Copyright 2005 Jonathan Ernst
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21
 *
22 23 24 25 26
 */

#include <stdio.h>
#include <string.h>
#include <windows.h>
27
#include <shlwapi.h>
28
#include "resource.h"
29
#include "regstr.h"
30 31 32
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
33 34

typedef struct {
35
    WCHAR *key;
36
    WCHAR *descr;
37
    WCHAR *command;
38 39
    int active;
} uninst_entry;
40 41 42 43
static uninst_entry *entries = NULL;
static unsigned int numentries = 0;
static int list_need_update = 1;
static int oldsel = -1;
44
static WCHAR *sFilter;
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
static WCHAR sAppName[MAX_STRING_LEN];
static WCHAR sAboutTitle[MAX_STRING_LEN];
static WCHAR sAbout[MAX_STRING_LEN];
static WCHAR sRegistryKeyNotAvailable[MAX_STRING_LEN];
static WCHAR sUninstallFailed[MAX_STRING_LEN];

static int FetchUninstallInformation(void);
static void UninstallProgram(void);
static void UpdateList(HWND hList);
static int cmp_by_name(const void *a, const void *b);
static INT_PTR CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);


static const WCHAR BackSlashW[] = { '\\', 0 };
static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
static const WCHAR PathUninstallW[] = {
        'S','o','f','t','w','a','r','e','\\',
        'M','i','c','r','o','s','o','f','t','\\',
        'W','i','n','d','o','w','s','\\',
        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
        'U','n','i','n','s','t','a','l','l',0 };
static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};

68

69 70 71 72
/**
 * Used to output program list when used with --list
 */
static void ListUninstallPrograms(void)
73
{
74
    unsigned int i;
75
    int lenDescr, lenKey;
76
    char *descr;
77
    char *key;
78

79
    FetchUninstallInformation();
80 81

    for (i=0; i < numentries; i++)
82
    {
83 84 85 86 87 88 89
        lenDescr = WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, NULL, 0, NULL, NULL); 
        lenKey = WideCharToMultiByte(CP_UNIXCP, 0, entries[i].key, -1, NULL, 0, NULL, NULL); 
        descr = HeapAlloc(GetProcessHeap(), 0, lenDescr);
        key = HeapAlloc(GetProcessHeap(), 0, lenKey);
        WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, descr, lenDescr, NULL, NULL);
        WideCharToMultiByte(CP_UNIXCP, 0, entries[i].key, -1, key, lenKey, NULL, NULL);
        printf("%s|||%s\n", key, descr);
90
        HeapFree(GetProcessHeap(), 0, descr);
91
        HeapFree(GetProcessHeap(), 0, key);
92
    }
93 94 95
}


96
static void RemoveSpecificProgram(WCHAR *nameW)
97
{
98
    unsigned int i;
99 100
    int lenName;
    char *name;
101

102
    FetchUninstallInformation();
103 104 105

    for (i=0; i < numentries; i++)
    {
106
        if (lstrcmpW(entries[i].key, nameW) == 0)
107 108 109 110 111 112 113 114 115 116
        {
            entries[i].active++;
            break;
        }
    }

    if (i < numentries)
        UninstallProgram();
    else
    {
117 118 119
        lenName = WideCharToMultiByte(CP_UNIXCP, 0, nameW, -1, NULL, 0, NULL, NULL); 
        name = HeapAlloc(GetProcessHeap(), 0, lenName);
        WideCharToMultiByte(CP_UNIXCP, 0, nameW, -1, name, lenName, NULL, NULL);
120
        fprintf(stderr, "Error: could not match application [%s]\n", name);
121
        HeapFree(GetProcessHeap(), 0, name);
122 123 124
    }
}

125 126

int wmain(int argc, WCHAR *argv[])
127
{
128 129 130 131
    LPCWSTR token = NULL;
    HINSTANCE hInst = GetModuleHandleW(0);
    static const WCHAR listW[] = { '-','-','l','i','s','t',0 };
    static const WCHAR removeW[] = { '-','-','r','e','m','o','v','e',0 };
132
    int i = 1;
133

134
    while( i<argc )
135
    {
136
        token = argv[i++];
137
        
138
        /* Handle requests just to list the applications */
139
        if( !lstrcmpW( token, listW ) )
140 141 142 143
        {
            ListUninstallPrograms();
            return 0;
        }
144
        else if( !lstrcmpW( token, removeW ) )
145 146 147 148 149 150 151 152 153 154 155 156
        {
            if( i >= argc )
            {
                WINE_ERR( "The remove option requires a parameter.\n");
                return 1;
            }

            RemoveSpecificProgram( argv[i++] );
            return 0;
        }
        else 
        {
157
            WINE_ERR( "unknown option %s\n",wine_dbgstr_w(token));
158 159
            return 1;
        }
160 161
    }

162 163 164 165 166 167 168 169
    /* Load MessageBox's strings */
    LoadStringW(hInst, IDS_APPNAME, sAppName, sizeof(sAppName)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_ABOUTTITLE, sAboutTitle, sizeof(sAboutTitle)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_ABOUT, sAbout, sizeof(sAbout)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_REGISTRYKEYNOTAVAILABLE, sRegistryKeyNotAvailable, sizeof(sRegistryKeyNotAvailable)/sizeof(WCHAR));
    LoadStringW(hInst, IDS_UNINSTALLFAILED, sUninstallFailed, sizeof(sUninstallFailed)/sizeof(WCHAR));

    return DialogBoxW(hInst, MAKEINTRESOURCEW(IDD_UNINSTALLER), NULL, DlgProc);
170 171
}

172 173 174 175 176

/**
 * Used to sort entries by name.
 */
static int cmp_by_name(const void *a, const void *b)
177
{
Eric Pouech's avatar
Eric Pouech committed
178
    return lstrcmpiW(((const uninst_entry *)a)->descr, ((const uninst_entry *)b)->descr);
179 180
}

181 182 183 184 185

/**
 * Fetch informations from the uninstall key.
 */
static int FetchUninstallInformation(void)
186 187 188
{
    HKEY hkeyUninst, hkeyApp;
    int i;
189
    DWORD sizeOfSubKeyName, displen, uninstlen;
190 191 192 193
    WCHAR subKeyName[256];
    WCHAR key_app[1024];
    WCHAR *p;
  
194 195
    numentries = 0;
    oldsel = -1;
196 197
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, PathUninstallW, 0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS)
        return 0;
198

199
    if (!entries)
200
        entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
201

202 203 204
    lstrcpyW(key_app, PathUninstallW);
    lstrcatW(key_app, BackSlashW);
    p = key_app+lstrlenW(PathUninstallW)+1;
205 206

    sizeOfSubKeyName = 255;
207
    for (i=0; RegEnumKeyExW( hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS; ++i)
208
    {
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        lstrcpyW(p, subKeyName);
        RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
        if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) == ERROR_SUCCESS)
         && (RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
        {
            numentries++;
            entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
            entries[numentries-1].key = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(subKeyName)+1)*sizeof(WCHAR));
            lstrcpyW(entries[numentries-1].key, subKeyName);
            entries[numentries-1].descr = HeapAlloc(GetProcessHeap(), 0, displen);
            RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)entries[numentries-1].descr, &displen);
            entries[numentries-1].command = HeapAlloc(GetProcessHeap(), 0, uninstlen);
            entries[numentries-1].active = 0;
            RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0, (LPBYTE)entries[numentries-1].command, &uninstlen);
            WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
            numentries, wine_dbgstr_w(entries[numentries-1].key), wine_dbgstr_w(entries[numentries-1].descr), wine_dbgstr_w(entries[numentries-1].command));
225 226
            if(sFilter != NULL && StrStrIW(entries[numentries-1].descr,sFilter)==NULL)
                numentries--;
227 228 229
        }
        RegCloseKey(hkeyApp);
        sizeOfSubKeyName = 255;
230
    }
231
    qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
232 233 234 235
    RegCloseKey(hkeyUninst);
    return 1;
}

236 237

static void UninstallProgram(void)
238
{
239
    unsigned int i;
240
    WCHAR errormsg[1024];
241
    BOOL res;
242
    STARTUPINFOW si;
243 244 245 246 247
    PROCESS_INFORMATION info;
    DWORD exit_code;
    HKEY hkey;
    for (i=0; i < numentries; i++)
    {
248 249 250 251 252 253 254 255 256 257 258
        if (!(entries[i].active)) /* don't uninstall this one */
            continue;
        WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries[i].descr));
        memset(&si, 0, sizeof(STARTUPINFOW));
        si.cb = sizeof(STARTUPINFOW);
        si.wShowWindow = SW_NORMAL;
        res = CreateProcessW(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
        if (res)
        {   /* wait for the process to exit */
            WaitForSingleObject(info.hProcess, INFINITE);
            res = GetExitCodeProcess(info.hProcess, &exit_code);
259
            WINE_TRACE("%d: %08x\n", res, exit_code);
260 261 262 263 264 265 266 267 268 269 270 271
        }
        else
        {
            wsprintfW(errormsg, sUninstallFailed, entries[i].command);
            if(MessageBoxW(0, errormsg, sAppName, MB_YESNO | MB_ICONQUESTION)==IDYES)
            {
                /* delete the application's uninstall entry */
                RegOpenKeyExW(HKEY_LOCAL_MACHINE, PathUninstallW, 0, KEY_READ, &hkey);
                RegDeleteKeyW(hkey, entries[i].key);
                RegCloseKey(hkey);
            }
        }
272
    }
273
    WINE_TRACE("finished uninstall phase.\n");
274
    list_need_update = 1;
275 276
}

277 278

static INT_PTR CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
279
{
280
    TEXTMETRICW tm;
281
    HDC hdc;
282 283 284 285 286 287 288 289 290 291 292 293
    HWND hList = GetDlgItem(hwnd, IDC_LIST);
    switch(Message)
    {
        case WM_INITDIALOG:
            hdc = GetDC(hwnd);
            GetTextMetricsW(hdc, &tm);
            UpdateList(hList);
            ReleaseDC(hwnd, hdc);
            break;
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
294 295 296 297 298 299 300 301 302
                case IDC_FILTER:
                {
                    if (HIWORD(wParam) == EN_CHANGE)
                    {
                        int len = GetWindowTextLengthW(GetDlgItem(hwnd, IDC_FILTER));
                        list_need_update = 1;
                        if(len > 0)
                        {
                            sFilter = (WCHAR*)GlobalAlloc(GPTR, (len + 1)*sizeof(WCHAR));
303
                            GetDlgItemTextW(hwnd, IDC_FILTER, sFilter, len + 1);
304 305 306 307 308 309
                        }
                        else sFilter = NULL;
                        UpdateList(hList);
                    }
                    break;
                }
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
                case IDC_UNINSTALL:
                {
                    int count = SendMessageW(hList, LB_GETSELCOUNT, 0, 0);
                    if(count != 0)
                    {
                        UninstallProgram();
                        UpdateList(hList);
                    }
                    break;
                }
                case IDC_LIST:
                    if (HIWORD(wParam) == LBN_SELCHANGE)
                    {
                       int sel = SendMessageW(hList, LB_GETCURSEL, 0, 0);
                       if (oldsel != -1)
                       {
                           entries[oldsel].active ^= 1; /* toggle */
                           WINE_TRACE("toggling %d old %s\n", entries[oldsel].active,
                           wine_dbgstr_w(entries[oldsel].descr));
                       }
                       entries[sel].active ^= 1; /* toggle */
                       WINE_TRACE("toggling %d %s\n", entries[sel].active,
                       wine_dbgstr_w(entries[oldsel].descr));
                       oldsel = sel;
                   }
                    break;
                case IDC_ABOUT:
                    MessageBoxW(0, sAbout, sAboutTitle, MB_OK);
                    break;
339
                case IDCANCEL:
340 341 342 343 344 345 346 347 348 349
                case IDC_EXIT:
                    EndDialog(hwnd, 0);
                    break;
            }
            break;
        default:
            return FALSE;
    }
    return TRUE;
}
350

351

352 353 354 355 356 357 358 359
static void UpdateList(HWND hList)
{
    unsigned int i;
    if (list_need_update)
    {
        int prevsel;
        prevsel = SendMessageW(hList, LB_GETCURSEL, 0, 0);
        if (!(FetchUninstallInformation()))
360
        {
361
            MessageBoxW(0, sRegistryKeyNotAvailable, sAppName, MB_OK);
362 363
            PostQuitMessage(0);
            return;
364
        }
365 366 367 368 369 370 371 372 373 374 375 376
        SendMessageW(hList, LB_RESETCONTENT, 0, 0);
        SendMessageW(hList, WM_SETREDRAW, FALSE, 0);
        for (i=0; i < numentries; i++)
        {
            WINE_TRACE("adding %s\n", wine_dbgstr_w(entries[i].descr));
            SendMessageW(hList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
        }
        WINE_TRACE("setting prevsel %d\n", prevsel);
        if (prevsel != -1)
            SendMessageW(hList, LB_SETCURSEL, prevsel, 0 );
        SendMessageW(hList, WM_SETREDRAW, TRUE, 0);
        list_need_update = 0;
377 378
    }
}