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
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller
);
35 extern void WINAPI
Control_RunDLL(HWND hWnd
, HINSTANCE hInst
, LPCSTR cmd
, DWORD nCmdShow
);
44 static uninst_entry
*entries
= NULL
;
45 static unsigned int numentries
= 0;
46 static int oldsel
= -1;
47 static WCHAR
*sFilter
;
50 static int FetchUninstallInformation(void);
51 static void UninstallProgram(void);
52 static const WCHAR PathUninstallW
[] = L
"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
55 static void WINAPIV
output_message(BOOL with_usage
, unsigned int id
, ...)
60 current_lcid
= GetThreadLocale();
61 if (silent
) /* force en-US not to have localized strings */
62 SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
), SORT_DEFAULT
));
64 if (LoadStringW(GetModuleHandleW(NULL
), id
, fmt
, ARRAY_SIZE(fmt
)) &&
65 (!with_usage
|| LoadStringW(GetModuleHandleW(NULL
), STRING_USAGE
, fmt
+ wcslen(fmt
), ARRAY_SIZE(fmt
) - wcslen(fmt
))))
71 va_start(va_args
, id
);
72 len
= FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ALLOCATE_BUFFER
,
73 fmt
, 0, 0, (LPWSTR
)&str
, 0, &va_args
);
74 if (len
> 0 || GetLastError() == ERROR_NO_WORK_DONE
)
79 MessageBoxW(NULL
, str
, MAKEINTRESOURCEW(IDS_APPNAME
), MB_OK
| MB_ICONSTOP
);
83 WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt
));
87 WINE_FIXME("LoadString failed with %ld\n", GetLastError());
89 SetThreadLocale(current_lcid
);
93 * Used to output program list when used with --list
95 static void ListUninstallPrograms(void)
99 FetchUninstallInformation();
101 setlocale(LC_ALL
, "en-US");
102 for (i
=0; i
< numentries
; i
++)
103 MESSAGE("%ls|||%ls\n", entries
[i
].key
, entries
[i
].descr
);
107 static void RemoveSpecificProgram(WCHAR
*nameW
)
111 FetchUninstallInformation();
113 for (i
=0; i
< numentries
; i
++)
115 if (CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, entries
[i
].key
, -1, nameW
, -1) == CSTR_EQUAL
)
125 output_message(FALSE
, STRING_NO_APP_MATCH
, nameW
);
129 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
131 LPCWSTR token
= NULL
;
135 if (IsWow64Process( GetCurrentProcess(), &is_wow64
) && is_wow64
)
138 PROCESS_INFORMATION pi
;
139 WCHAR filename
[MAX_PATH
];
143 memset( &si
, 0, sizeof(si
) );
145 GetSystemDirectoryW( filename
, MAX_PATH
);
146 wcscat( filename
, L
"\\uninstaller.exe" );
148 Wow64DisableWow64FsRedirection( &redir
);
149 if (CreateProcessW( filename
, GetCommandLineW(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
151 WINE_TRACE( "restarting %s\n", wine_dbgstr_w(filename
) );
152 WaitForSingleObject( pi
.hProcess
, INFINITE
);
153 GetExitCodeProcess( pi
.hProcess
, &exit_code
);
154 ExitProcess( exit_code
);
156 else WINE_ERR( "failed to restart 64-bit %s, err %ld\n", wine_dbgstr_w(filename
), GetLastError() );
157 Wow64RevertWow64FsRedirection( redir
);
160 InitCommonControls();
166 if( !lstrcmpW( token
, L
"--help" ) )
168 output_message(TRUE
, STRING_HEADER
);
171 else if( !lstrcmpW( token
, L
"--silent" ) )
175 else if( !lstrcmpW( token
, L
"--list" ) )
177 ListUninstallPrograms();
180 else if( !lstrcmpW( token
, L
"--remove" ) )
184 output_message(FALSE
, STRING_PARAMETER_REQUIRED
);
188 RemoveSpecificProgram( argv
[i
++] );
193 output_message(FALSE
, STRING_INVALID_OPTION
, token
);
198 /* Start the GUI control panel */
199 Control_RunDLL(GetDesktopWindow(), 0, "appwiz.cpl", SW_SHOW
);
205 * Used to sort entries by name.
207 static int __cdecl
cmp_by_name(const void *a
, const void *b
)
209 return lstrcmpiW(((const uninst_entry
*)a
)->descr
, ((const uninst_entry
*)b
)->descr
);
214 * Fetch information from the uninstall key.
216 static int FetchFromRootKey(HKEY root
)
220 DWORD sizeOfSubKeyName
, displen
, uninstlen
, value
, type
, size
;
221 WCHAR subKeyName
[256];
223 sizeOfSubKeyName
= 255;
224 for (i
=0; RegEnumKeyExW( root
, i
, subKeyName
, &sizeOfSubKeyName
, NULL
, NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
; ++i
)
226 RegOpenKeyExW(root
, subKeyName
, 0, KEY_READ
, &hkeyApp
);
227 size
= sizeof(value
);
228 if (!RegQueryValueExW(hkeyApp
, L
"SystemComponent", NULL
, &type
, (BYTE
*)&value
, &size
) &&
229 type
== REG_DWORD
&& value
== 1)
231 RegCloseKey(hkeyApp
);
232 sizeOfSubKeyName
= 255;
235 if (!RegQueryValueExW(hkeyApp
, L
"DisplayName", NULL
, NULL
, NULL
, &displen
))
239 size
= sizeof(value
);
240 if (!RegQueryValueExW(hkeyApp
, L
"WindowsInstaller", NULL
, &type
, (BYTE
*)&value
, &size
) &&
241 type
== REG_DWORD
&& value
== 1)
243 command
= malloc(sizeof(L
"msiexec /x") + wcslen(subKeyName
) * sizeof(WCHAR
));
244 wsprintfW(command
, L
"msiexec /x%s", subKeyName
);
246 else if (!RegQueryValueExW(hkeyApp
, L
"UninstallString", NULL
, NULL
, NULL
, &uninstlen
))
248 command
= malloc(uninstlen
);
249 RegQueryValueExW(hkeyApp
, L
"UninstallString", 0, 0, (BYTE
*)command
, &uninstlen
);
253 RegCloseKey(hkeyApp
);
254 sizeOfSubKeyName
= 255;
258 entries
= realloc(entries
, numentries
* sizeof(uninst_entry
));
259 entries
[numentries
-1].root
= root
;
260 entries
[numentries
-1].key
= wcsdup(subKeyName
);
261 entries
[numentries
-1].descr
= malloc(displen
);
262 RegQueryValueExW(hkeyApp
, L
"DisplayName", 0, 0, (BYTE
*)entries
[numentries
-1].descr
, &displen
);
263 entries
[numentries
-1].command
= command
;
264 entries
[numentries
-1].active
= 0;
265 WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
266 numentries
, wine_dbgstr_w(entries
[numentries
-1].key
), wine_dbgstr_w(entries
[numentries
-1].descr
), wine_dbgstr_w(entries
[numentries
-1].command
));
267 if(sFilter
!= NULL
&& StrStrIW(entries
[numentries
-1].descr
,sFilter
)==NULL
)
270 RegCloseKey(hkeyApp
);
271 sizeOfSubKeyName
= 255;
277 static int FetchUninstallInformation(void)
279 static const BOOL is_64bit
= sizeof(void *) > sizeof(int);
286 entries
= malloc(sizeof(uninst_entry
));
288 if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE
, PathUninstallW
, 0, KEY_READ
, &root
))
290 rc
|= FetchFromRootKey(root
);
294 !RegOpenKeyExW(HKEY_LOCAL_MACHINE
, PathUninstallW
, 0, KEY_READ
|KEY_WOW64_32KEY
, &root
))
296 rc
|= FetchFromRootKey(root
);
299 if (!RegOpenKeyExW(HKEY_CURRENT_USER
, PathUninstallW
, 0, KEY_READ
, &root
))
301 rc
|= FetchFromRootKey(root
);
305 qsort(entries
, numentries
, sizeof(uninst_entry
), cmp_by_name
);
309 static void UninstallProgram(void)
312 WCHAR errormsg
[1024];
315 PROCESS_INFORMATION info
;
318 for (i
=0; i
< numentries
; i
++)
320 if (!(entries
[i
].active
)) /* don't uninstall this one */
322 WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries
[i
].descr
));
323 memset(&si
, 0, sizeof(STARTUPINFOW
));
324 si
.cb
= sizeof(STARTUPINFOW
);
325 si
.wShowWindow
= SW_NORMAL
;
326 res
= CreateProcessW(NULL
, entries
[i
].command
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &info
);
328 { /* wait for the process to exit */
329 WaitForSingleObject(info
.hProcess
, INFINITE
);
330 res
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
331 WINE_TRACE("%d: %08lx\n", res
, exit_code
);
335 WCHAR sAppName
[MAX_STRING_LEN
];
336 WCHAR sUninstallFailed
[MAX_STRING_LEN
];
337 HINSTANCE hInst
= GetModuleHandleW(0);
339 LoadStringW(hInst
, IDS_APPNAME
, sAppName
, ARRAY_SIZE(sAppName
));
340 LoadStringW(hInst
, IDS_UNINSTALLFAILED
, sUninstallFailed
, ARRAY_SIZE(sUninstallFailed
));
341 wsprintfW(errormsg
, sUninstallFailed
, entries
[i
].command
);
342 if(MessageBoxW(0, errormsg
, sAppName
, MB_YESNO
| MB_ICONQUESTION
)==IDYES
)
344 /* delete the application's uninstall entry */
345 RegOpenKeyExW(entries
[i
].root
, PathUninstallW
, 0, KEY_READ
, &hkey
);
346 RegDeleteKeyW(hkey
, entries
[i
].key
);
351 WINE_TRACE("finished uninstall phase.\n");