4 * Copyright 2000 Andreas Mohr
5 * Copyright 2004 Hannu Valtonen
6 * Copyright 2005 Jonathan Ernst
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * 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
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller
);
33 extern void WINAPI
Control_RunDLL(HWND hWnd
, HINSTANCE hInst
, LPCSTR cmd
, DWORD nCmdShow
);
42 static uninst_entry
*entries
= NULL
;
43 static unsigned int numentries
= 0;
44 static int oldsel
= -1;
45 static WCHAR
*sFilter
;
47 static int FetchUninstallInformation(void);
48 static void UninstallProgram(void);
50 static const WCHAR DisplayNameW
[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
51 static const WCHAR PathUninstallW
[] = {
52 'S','o','f','t','w','a','r','e','\\',
53 'M','i','c','r','o','s','o','f','t','\\',
54 'W','i','n','d','o','w','s','\\',
55 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
56 'U','n','i','n','s','t','a','l','l',0 };
57 static const WCHAR UninstallCommandlineW
[] = {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
58 static const WCHAR WindowsInstallerW
[] = {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
59 static const WCHAR SystemComponentW
[] = {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
61 static void output_writeconsole(const WCHAR
*str
, DWORD len
)
63 DWORD written
, ret
, lenA
;
66 ret
= WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE
), str
, len
, &written
, NULL
);
69 /* WriteConsole fails if its output is redirected to a file.
70 * If this occurs, we should use an OEM codepage and call WriteFile.
72 lenA
= WideCharToMultiByte(GetConsoleOutputCP(), 0, str
, len
, NULL
, 0, NULL
, NULL
);
73 strA
= HeapAlloc(GetProcessHeap(), 0, lenA
);
76 WideCharToMultiByte(GetConsoleOutputCP(), 0, str
, len
, strA
, lenA
, NULL
, NULL
);
77 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), strA
, lenA
, &written
, FALSE
);
78 HeapFree(GetProcessHeap(), 0, strA
);
82 static void output_formatstring(const WCHAR
*fmt
, __ms_va_list va_args
)
87 SetLastError(NO_ERROR
);
88 len
= FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ALLOCATE_BUFFER
,
89 fmt
, 0, 0, (LPWSTR
)&str
, 0, &va_args
);
90 if (len
== 0 && GetLastError() != NO_ERROR
)
92 WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt
));
95 output_writeconsole(str
, len
);
99 static void WINAPIV
output_message(unsigned int id
, ...)
102 __ms_va_list va_args
;
104 if (!LoadStringW(GetModuleHandleW(NULL
), id
, fmt
, ARRAY_SIZE(fmt
)))
106 WINE_FIXME("LoadString failed with %d\n", GetLastError());
109 __ms_va_start(va_args
, id
);
110 output_formatstring(fmt
, va_args
);
111 __ms_va_end(va_args
);
114 static void WINAPIV
output_array(WCHAR
*fmt
, ...)
116 __ms_va_list va_args
;
118 __ms_va_start(va_args
, fmt
);
119 output_formatstring(fmt
, va_args
);
120 __ms_va_end(va_args
);
124 * Used to output program list when used with --list
126 static void ListUninstallPrograms(void)
129 static WCHAR fmtW
[] = {'%','1','|','|','|','%','2','\n',0};
131 FetchUninstallInformation();
133 for (i
=0; i
< numentries
; i
++)
134 output_array(fmtW
, entries
[i
].key
, entries
[i
].descr
);
138 static void RemoveSpecificProgram(WCHAR
*nameW
)
142 FetchUninstallInformation();
144 for (i
=0; i
< numentries
; i
++)
146 if (CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, entries
[i
].key
, -1, nameW
, -1) == CSTR_EQUAL
)
156 output_message(STRING_NO_APP_MATCH
, nameW
);
160 int wmain(int argc
, WCHAR
*argv
[])
162 LPCWSTR token
= NULL
;
163 static const WCHAR helpW
[] = { '-','-','h','e','l','p',0 };
164 static const WCHAR listW
[] = { '-','-','l','i','s','t',0 };
165 static const WCHAR removeW
[] = { '-','-','r','e','m','o','v','e',0 };
169 if (IsWow64Process( GetCurrentProcess(), &is_wow64
) && is_wow64
)
172 PROCESS_INFORMATION pi
;
173 WCHAR filename
[MAX_PATH
];
177 memset( &si
, 0, sizeof(si
) );
179 GetModuleFileNameW( 0, filename
, MAX_PATH
);
181 Wow64DisableWow64FsRedirection( &redir
);
182 if (CreateProcessW( filename
, GetCommandLineW(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
184 WINE_TRACE( "restarting %s\n", wine_dbgstr_w(filename
) );
185 WaitForSingleObject( pi
.hProcess
, INFINITE
);
186 GetExitCodeProcess( pi
.hProcess
, &exit_code
);
187 ExitProcess( exit_code
);
189 else WINE_ERR( "failed to restart 64-bit %s, err %d\n", wine_dbgstr_w(filename
), GetLastError() );
190 Wow64RevertWow64FsRedirection( redir
);
197 if( !lstrcmpW( token
, helpW
) )
199 output_message(STRING_HEADER
);
200 output_message(STRING_USAGE
);
203 else if( !lstrcmpW( token
, listW
) )
205 ListUninstallPrograms();
208 else if( !lstrcmpW( token
, removeW
) )
212 output_message(STRING_PARAMETER_REQUIRED
);
216 RemoveSpecificProgram( argv
[i
++] );
221 output_message(STRING_INVALID_OPTION
, token
);
226 /* Start the GUI control panel */
227 Control_RunDLL(GetDesktopWindow(), 0, "appwiz.cpl", SW_SHOW
);
233 * Used to sort entries by name.
235 static int __cdecl
cmp_by_name(const void *a
, const void *b
)
237 return lstrcmpiW(((const uninst_entry
*)a
)->descr
, ((const uninst_entry
*)b
)->descr
);
242 * Fetch information from the uninstall key.
244 static int FetchFromRootKey(HKEY root
)
248 DWORD sizeOfSubKeyName
, displen
, uninstlen
, value
, type
, size
;
249 WCHAR subKeyName
[256];
251 sizeOfSubKeyName
= 255;
252 for (i
=0; RegEnumKeyExW( root
, i
, subKeyName
, &sizeOfSubKeyName
, NULL
, NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
; ++i
)
254 RegOpenKeyExW(root
, subKeyName
, 0, KEY_READ
, &hkeyApp
);
255 size
= sizeof(value
);
256 if (!RegQueryValueExW(hkeyApp
, SystemComponentW
, NULL
, &type
, (LPBYTE
)&value
, &size
) &&
257 type
== REG_DWORD
&& value
== 1)
259 RegCloseKey(hkeyApp
);
260 sizeOfSubKeyName
= 255;
263 if (!RegQueryValueExW(hkeyApp
, DisplayNameW
, NULL
, NULL
, NULL
, &displen
))
267 size
= sizeof(value
);
268 if (!RegQueryValueExW(hkeyApp
, WindowsInstallerW
, NULL
, &type
, (LPBYTE
)&value
, &size
) &&
269 type
== REG_DWORD
&& value
== 1)
271 static const WCHAR fmtW
[] = {'m','s','i','e','x','e','c',' ','/','x','%','s',0};
272 command
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(fmtW
) + lstrlenW(subKeyName
)) * sizeof(WCHAR
));
273 wsprintfW(command
, fmtW
, subKeyName
);
275 else if (!RegQueryValueExW(hkeyApp
, UninstallCommandlineW
, NULL
, NULL
, NULL
, &uninstlen
))
277 command
= HeapAlloc(GetProcessHeap(), 0, uninstlen
);
278 RegQueryValueExW(hkeyApp
, UninstallCommandlineW
, 0, 0, (LPBYTE
)command
, &uninstlen
);
282 RegCloseKey(hkeyApp
);
283 sizeOfSubKeyName
= 255;
287 entries
= HeapReAlloc(GetProcessHeap(), 0, entries
, numentries
*sizeof(uninst_entry
));
288 entries
[numentries
-1].root
= root
;
289 entries
[numentries
-1].key
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(subKeyName
)+1)*sizeof(WCHAR
));
290 lstrcpyW(entries
[numentries
-1].key
, subKeyName
);
291 entries
[numentries
-1].descr
= HeapAlloc(GetProcessHeap(), 0, displen
);
292 RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0, (LPBYTE
)entries
[numentries
-1].descr
, &displen
);
293 entries
[numentries
-1].command
= command
;
294 entries
[numentries
-1].active
= 0;
295 WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
296 numentries
, wine_dbgstr_w(entries
[numentries
-1].key
), wine_dbgstr_w(entries
[numentries
-1].descr
), wine_dbgstr_w(entries
[numentries
-1].command
));
297 if(sFilter
!= NULL
&& StrStrIW(entries
[numentries
-1].descr
,sFilter
)==NULL
)
300 RegCloseKey(hkeyApp
);
301 sizeOfSubKeyName
= 255;
307 static int FetchUninstallInformation(void)
309 static const BOOL is_64bit
= sizeof(void *) > sizeof(int);
316 entries
= HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry
));
318 if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE
, PathUninstallW
, 0, KEY_READ
, &root
))
320 rc
|= FetchFromRootKey(root
);
324 !RegOpenKeyExW(HKEY_LOCAL_MACHINE
, PathUninstallW
, 0, KEY_READ
|KEY_WOW64_32KEY
, &root
))
326 rc
|= FetchFromRootKey(root
);
329 if (!RegOpenKeyExW(HKEY_CURRENT_USER
, PathUninstallW
, 0, KEY_READ
, &root
))
331 rc
|= FetchFromRootKey(root
);
335 qsort(entries
, numentries
, sizeof(uninst_entry
), cmp_by_name
);
339 static void UninstallProgram(void)
342 WCHAR errormsg
[1024];
345 PROCESS_INFORMATION info
;
348 for (i
=0; i
< numentries
; i
++)
350 if (!(entries
[i
].active
)) /* don't uninstall this one */
352 WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries
[i
].descr
));
353 memset(&si
, 0, sizeof(STARTUPINFOW
));
354 si
.cb
= sizeof(STARTUPINFOW
);
355 si
.wShowWindow
= SW_NORMAL
;
356 res
= CreateProcessW(NULL
, entries
[i
].command
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &info
);
358 { /* wait for the process to exit */
359 WaitForSingleObject(info
.hProcess
, INFINITE
);
360 res
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
361 WINE_TRACE("%d: %08x\n", res
, exit_code
);
365 WCHAR sAppName
[MAX_STRING_LEN
];
366 WCHAR sUninstallFailed
[MAX_STRING_LEN
];
367 HINSTANCE hInst
= GetModuleHandleW(0);
369 LoadStringW(hInst
, IDS_APPNAME
, sAppName
, ARRAY_SIZE(sAppName
));
370 LoadStringW(hInst
, IDS_UNINSTALLFAILED
, sUninstallFailed
, ARRAY_SIZE(sUninstallFailed
));
371 wsprintfW(errormsg
, sUninstallFailed
, entries
[i
].command
);
372 if(MessageBoxW(0, errormsg
, sAppName
, MB_YESNO
| MB_ICONQUESTION
)==IDYES
)
374 /* delete the application's uninstall entry */
375 RegOpenKeyExW(entries
[i
].root
, PathUninstallW
, 0, KEY_READ
, &hkey
);
376 RegDeleteKeyW(hkey
, entries
[i
].key
);
381 WINE_TRACE("finished uninstall phase.\n");