main.c 11.3 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
 */

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

WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
32

33 34
extern void WINAPI Control_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow);

35
typedef struct {
36
    HKEY  root;
37
    WCHAR *key;
38
    WCHAR *descr;
39
    WCHAR *command;
40 41
    int active;
} uninst_entry;
42 43 44
static uninst_entry *entries = NULL;
static unsigned int numentries = 0;
static int oldsel = -1;
45
static WCHAR *sFilter;
46 47 48 49 50 51 52 53 54 55 56 57 58

static int FetchUninstallInformation(void);
static void UninstallProgram(void);
static int cmp_by_name(const void *a, const void *b);

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};
59
static const WCHAR WindowsInstallerW[] = {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
60
static const WCHAR SystemComponentW[] = {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
61

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
static void output_writeconsole(const WCHAR *str, DWORD len)
{
    DWORD written, ret, lenA;
    char *strA;

    ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, len, &written, NULL);
    if (ret) return;

    /* WriteConsole fails if its output is redirected to a file.
     * If this occurs, we should use an OEM codepage and call WriteFile.
     */
    lenA = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, len, NULL, 0, NULL, NULL);
    strA = HeapAlloc(GetProcessHeap(), 0, lenA);
    if (strA)
    {
        WideCharToMultiByte(GetConsoleOutputCP(), 0, str, len, strA, lenA, NULL, NULL);
        WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), strA, lenA, &written, FALSE);
        HeapFree(GetProcessHeap(), 0, strA);
    }
}

83 84 85
static void output_formatstring(const WCHAR *fmt, __ms_va_list va_args)
{
    WCHAR *str;
86
    DWORD len;
87 88 89 90 91 92 93 94 95

    SetLastError(NO_ERROR);
    len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
                         fmt, 0, 0, (LPWSTR)&str, 0, &va_args);
    if (len == 0 && GetLastError() != NO_ERROR)
    {
        WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
        return;
    }
96
    output_writeconsole(str, len);
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    LocalFree(str);
}

static void __cdecl output_message(unsigned int id, ...)
{
    WCHAR fmt[1024];
    __ms_va_list va_args;

    if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, sizeof(fmt)/sizeof(fmt[0])))
    {
        WINE_FIXME("LoadString failed with %d\n", GetLastError());
        return;
    }
    __ms_va_start(va_args, id);
    output_formatstring(fmt, va_args);
    __ms_va_end(va_args);
}

115 116 117 118 119 120 121 122 123
static void __cdecl output_array(WCHAR *fmt, ...)
{
    __ms_va_list va_args;

    __ms_va_start(va_args, fmt);
    output_formatstring(fmt, va_args);
    __ms_va_end(va_args);
}

124 125 126 127
/**
 * Used to output program list when used with --list
 */
static void ListUninstallPrograms(void)
128
{
129
    unsigned int i;
130
    static WCHAR fmtW[] = {'%','1','|','|','|','%','2','\n',0};
131

132
    FetchUninstallInformation();
133 134

    for (i=0; i < numentries; i++)
135
        output_array(fmtW, entries[i].key, entries[i].descr);
136 137 138
}


139
static void RemoveSpecificProgram(WCHAR *nameW)
140
{
141
    unsigned int i;
142

143
    FetchUninstallInformation();
144 145 146

    for (i=0; i < numentries; i++)
    {
147
        if (CompareStringW(GetThreadLocale(), NORM_IGNORECASE, entries[i].key, -1, nameW, -1) == CSTR_EQUAL)
148 149 150 151 152 153 154 155 156
        {
            entries[i].active++;
            break;
        }
    }

    if (i < numentries)
        UninstallProgram();
    else
157
        output_message(STRING_NO_APP_MATCH, nameW);
158 159
}

160 161

int wmain(int argc, WCHAR *argv[])
162
{
163
    LPCWSTR token = NULL;
164
    static const WCHAR helpW[] = { '-','-','h','e','l','p',0 };
165 166
    static const WCHAR listW[] = { '-','-','l','i','s','t',0 };
    static const WCHAR removeW[] = { '-','-','r','e','m','o','v','e',0 };
167
    int i = 1;
168

169
    while( i<argc )
170
    {
171
        token = argv[i++];
172
        
173 174 175 176 177 178 179
        if( !lstrcmpW( token, helpW ) )
        {
            output_message(STRING_HEADER);
            output_message(STRING_USAGE);
            return 0;
        }
        else if( !lstrcmpW( token, listW ) )
180 181 182 183
        {
            ListUninstallPrograms();
            return 0;
        }
184
        else if( !lstrcmpW( token, removeW ) )
185 186 187
        {
            if( i >= argc )
            {
188
                output_message(STRING_PARAMETER_REQUIRED);
189 190 191 192 193 194 195 196
                return 1;
            }

            RemoveSpecificProgram( argv[i++] );
            return 0;
        }
        else 
        {
197
            output_message(STRING_INVALID_OPTION, token);
198 199
            return 1;
        }
200 201
    }

202 203 204
    /* Start the GUI control panel */
    Control_RunDLL(GetDesktopWindow(), 0, "appwiz.cpl", SW_SHOW);
    return 1;
205 206
}

207 208 209 210 211

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

216 217

/**
218
 * Fetch information from the uninstall key.
219
 */
220
static int FetchFromRootKey(HKEY root)
221
{
222
    HKEY hkeyApp;
223
    int i;
224
    DWORD sizeOfSubKeyName, displen, uninstlen, value, type, size;
225
    WCHAR subKeyName[256];
226 227

    sizeOfSubKeyName = 255;
228
    for (i=0; RegEnumKeyExW( root, i, subKeyName, &sizeOfSubKeyName, NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS; ++i)
229
    {
230
        RegOpenKeyExW(root, subKeyName, 0, KEY_READ, &hkeyApp);
231
        size = sizeof(value);
232 233 234 235 236 237 238
        if (!RegQueryValueExW(hkeyApp, SystemComponentW, NULL, &type, (LPBYTE)&value, &size) &&
            type == REG_DWORD && value == 1)
        {
            RegCloseKey(hkeyApp);
            sizeOfSubKeyName = 255;
            continue;
        }
239
        if (!RegQueryValueExW(hkeyApp, DisplayNameW, NULL, NULL, NULL, &displen))
240
        {
241 242
            WCHAR *command;

243 244 245
            size = sizeof(value);
            if (!RegQueryValueExW(hkeyApp, WindowsInstallerW, NULL, &type, (LPBYTE)&value, &size) &&
                type == REG_DWORD && value == 1)
246 247 248 249 250 251 252 253 254 255
            {
                static const WCHAR fmtW[] = {'m','s','i','e','x','e','c',' ','/','x','%','s',0};
                command = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fmtW) + lstrlenW(subKeyName)) * sizeof(WCHAR));
                wsprintfW(command, fmtW, subKeyName);
            }
            else if (!RegQueryValueExW(hkeyApp, UninstallCommandlineW, NULL, NULL, NULL, &uninstlen))
            {
                command = HeapAlloc(GetProcessHeap(), 0, uninstlen);
                RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0, (LPBYTE)command, &uninstlen);
            }
256 257 258 259 260 261
            else
            {
                RegCloseKey(hkeyApp);
                sizeOfSubKeyName = 255;
                continue;
            }
262 263
            numentries++;
            entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
264
            entries[numentries-1].root = root;
265 266 267 268
            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);
269
            entries[numentries-1].command = command;
270 271 272
            entries[numentries-1].active = 0;
            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));
273 274
            if(sFilter != NULL && StrStrIW(entries[numentries-1].descr,sFilter)==NULL)
                numentries--;
275 276 277
        }
        RegCloseKey(hkeyApp);
        sizeOfSubKeyName = 255;
278 279
    }
    return 1;
280

281 282
}

283 284
static int FetchUninstallInformation(void)
{
285 286 287
    static const BOOL is_64bit = sizeof(void *) > sizeof(int);
    int rc = 0;
    HKEY root;
288 289 290 291 292 293

    numentries = 0;
    oldsel = -1;
    if (!entries)
        entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
    if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, PathUninstallW, 0, KEY_READ, &root))
    {
        rc |= FetchFromRootKey(root);
        RegCloseKey(root);
    }
    if (is_64bit &&
        !RegOpenKeyExW(HKEY_LOCAL_MACHINE, PathUninstallW, 0, KEY_READ|KEY_WOW64_32KEY, &root))
    {
        rc |= FetchFromRootKey(root);
        RegCloseKey(root);
    }
    if (!RegOpenKeyExW(HKEY_CURRENT_USER, PathUninstallW, 0, KEY_READ, &root))
    {
        rc |= FetchFromRootKey(root);
        RegCloseKey(root);
    }
310 311 312 313

    qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
    return rc;
}
314 315

static void UninstallProgram(void)
316
{
317
    unsigned int i;
318
    WCHAR errormsg[1024];
319
    BOOL res;
320
    STARTUPINFOW si;
321 322 323 324 325
    PROCESS_INFORMATION info;
    DWORD exit_code;
    HKEY hkey;
    for (i=0; i < numentries; i++)
    {
326 327 328 329 330 331 332 333 334 335 336
        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);
337
            WINE_TRACE("%d: %08x\n", res, exit_code);
338 339 340
        }
        else
        {
341 342 343 344 345 346
            WCHAR sAppName[MAX_STRING_LEN];
            WCHAR sUninstallFailed[MAX_STRING_LEN];
            HINSTANCE hInst = GetModuleHandleW(0);

            LoadStringW(hInst, IDS_APPNAME, sAppName, sizeof(sAppName)/sizeof(WCHAR));
            LoadStringW(hInst, IDS_UNINSTALLFAILED, sUninstallFailed, sizeof(sUninstallFailed)/sizeof(WCHAR));
347 348 349 350
            wsprintfW(errormsg, sUninstallFailed, entries[i].command);
            if(MessageBoxW(0, errormsg, sAppName, MB_YESNO | MB_ICONQUESTION)==IDYES)
            {
                /* delete the application's uninstall entry */
351
                RegOpenKeyExW(entries[i].root, PathUninstallW, 0, KEY_READ, &hkey);
352 353 354 355
                RegDeleteKeyW(hkey, entries[i].key);
                RegCloseKey(hkey);
            }
        }
356
    }
357
    WINE_TRACE("finished uninstall phase.\n");
358
}