main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. * Uninstaller
  3. *
  4. * Copyright 2000 Andreas Mohr
  5. * Copyright 2004 Hannu Valtonen
  6. * Copyright 2005 Jonathan Ernst
  7. *
  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.
  12. *
  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.
  17. *
  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
  21. *
  22. */
  23. #include <string.h>
  24. #include <windows.h>
  25. #include <commctrl.h>
  26. #include <shlwapi.h>
  27. #include "resource.h"
  28. #include "regstr.h"
  29. #include "wine/debug.h"
  30. WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
  31. extern void WINAPI Control_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow);
  32. typedef struct {
  33. HKEY root;
  34. WCHAR *key;
  35. WCHAR *descr;
  36. WCHAR *command;
  37. int active;
  38. } uninst_entry;
  39. static uninst_entry *entries = NULL;
  40. static unsigned int numentries = 0;
  41. static int oldsel = -1;
  42. static WCHAR *sFilter;
  43. static int FetchUninstallInformation(void);
  44. static void UninstallProgram(void);
  45. static const WCHAR PathUninstallW[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  46. static void output_writeconsole(const WCHAR *str, DWORD len)
  47. {
  48. DWORD written, ret, lenA;
  49. char *strA;
  50. ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, len, &written, NULL);
  51. if (ret) return;
  52. /* WriteConsole fails if its output is redirected to a file.
  53. * If this occurs, we should use an OEM codepage and call WriteFile.
  54. */
  55. lenA = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, len, NULL, 0, NULL, NULL);
  56. strA = HeapAlloc(GetProcessHeap(), 0, lenA);
  57. if (strA)
  58. {
  59. WideCharToMultiByte(GetConsoleOutputCP(), 0, str, len, strA, lenA, NULL, NULL);
  60. WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), strA, lenA, &written, FALSE);
  61. HeapFree(GetProcessHeap(), 0, strA);
  62. }
  63. }
  64. static void output_formatstring(const WCHAR *fmt, va_list va_args)
  65. {
  66. WCHAR *str;
  67. DWORD len;
  68. len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  69. fmt, 0, 0, (LPWSTR)&str, 0, &va_args);
  70. if (len == 0 && GetLastError() != ERROR_NO_WORK_DONE)
  71. {
  72. WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
  73. return;
  74. }
  75. output_writeconsole(str, len);
  76. LocalFree(str);
  77. }
  78. static void WINAPIV output_message(unsigned int id, ...)
  79. {
  80. WCHAR fmt[1024];
  81. va_list va_args;
  82. if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
  83. {
  84. WINE_FIXME("LoadString failed with %d\n", GetLastError());
  85. return;
  86. }
  87. va_start(va_args, id);
  88. output_formatstring(fmt, va_args);
  89. va_end(va_args);
  90. }
  91. static void WINAPIV output_array(const WCHAR *fmt, ...)
  92. {
  93. va_list va_args;
  94. va_start(va_args, fmt);
  95. output_formatstring(fmt, va_args);
  96. va_end(va_args);
  97. }
  98. /**
  99. * Used to output program list when used with --list
  100. */
  101. static void ListUninstallPrograms(void)
  102. {
  103. unsigned int i;
  104. FetchUninstallInformation();
  105. for (i=0; i < numentries; i++)
  106. output_array(L"%1|||%2\n", entries[i].key, entries[i].descr);
  107. }
  108. static void RemoveSpecificProgram(WCHAR *nameW)
  109. {
  110. unsigned int i;
  111. FetchUninstallInformation();
  112. for (i=0; i < numentries; i++)
  113. {
  114. if (CompareStringW(GetThreadLocale(), NORM_IGNORECASE, entries[i].key, -1, nameW, -1) == CSTR_EQUAL)
  115. {
  116. entries[i].active++;
  117. break;
  118. }
  119. }
  120. if (i < numentries)
  121. UninstallProgram();
  122. else
  123. output_message(STRING_NO_APP_MATCH, nameW);
  124. }
  125. int __cdecl wmain(int argc, WCHAR *argv[])
  126. {
  127. LPCWSTR token = NULL;
  128. int i = 1;
  129. BOOL is_wow64;
  130. if (IsWow64Process( GetCurrentProcess(), &is_wow64 ) && is_wow64)
  131. {
  132. STARTUPINFOW si;
  133. PROCESS_INFORMATION pi;
  134. WCHAR filename[MAX_PATH];
  135. void *redir;
  136. DWORD exit_code;
  137. memset( &si, 0, sizeof(si) );
  138. si.cb = sizeof(si);
  139. GetSystemDirectoryW( filename, MAX_PATH );
  140. wcscat( filename, L"\\uninstaller.exe" );
  141. Wow64DisableWow64FsRedirection( &redir );
  142. if (CreateProcessW( filename, GetCommandLineW(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
  143. {
  144. WINE_TRACE( "restarting %s\n", wine_dbgstr_w(filename) );
  145. WaitForSingleObject( pi.hProcess, INFINITE );
  146. GetExitCodeProcess( pi.hProcess, &exit_code );
  147. ExitProcess( exit_code );
  148. }
  149. else WINE_ERR( "failed to restart 64-bit %s, err %d\n", wine_dbgstr_w(filename), GetLastError() );
  150. Wow64RevertWow64FsRedirection( redir );
  151. }
  152. InitCommonControls();
  153. while( i<argc )
  154. {
  155. token = argv[i++];
  156. if( !lstrcmpW( token, L"--help" ) )
  157. {
  158. output_message(STRING_HEADER);
  159. output_message(STRING_USAGE);
  160. return 0;
  161. }
  162. else if( !lstrcmpW( token, L"--list" ) )
  163. {
  164. ListUninstallPrograms();
  165. return 0;
  166. }
  167. else if( !lstrcmpW( token, L"--remove" ) )
  168. {
  169. if( i >= argc )
  170. {
  171. output_message(STRING_PARAMETER_REQUIRED);
  172. return 1;
  173. }
  174. RemoveSpecificProgram( argv[i++] );
  175. return 0;
  176. }
  177. else
  178. {
  179. output_message(STRING_INVALID_OPTION, token);
  180. return 1;
  181. }
  182. }
  183. /* Start the GUI control panel */
  184. Control_RunDLL(GetDesktopWindow(), 0, "appwiz.cpl", SW_SHOW);
  185. return 1;
  186. }
  187. /**
  188. * Used to sort entries by name.
  189. */
  190. static int __cdecl cmp_by_name(const void *a, const void *b)
  191. {
  192. return lstrcmpiW(((const uninst_entry *)a)->descr, ((const uninst_entry *)b)->descr);
  193. }
  194. /**
  195. * Fetch information from the uninstall key.
  196. */
  197. static int FetchFromRootKey(HKEY root)
  198. {
  199. HKEY hkeyApp;
  200. int i;
  201. DWORD sizeOfSubKeyName, displen, uninstlen, value, type, size;
  202. WCHAR subKeyName[256];
  203. sizeOfSubKeyName = 255;
  204. for (i=0; RegEnumKeyExW( root, i, subKeyName, &sizeOfSubKeyName, NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS; ++i)
  205. {
  206. RegOpenKeyExW(root, subKeyName, 0, KEY_READ, &hkeyApp);
  207. size = sizeof(value);
  208. if (!RegQueryValueExW(hkeyApp, L"SystemComponent", NULL, &type, (BYTE *)&value, &size) &&
  209. type == REG_DWORD && value == 1)
  210. {
  211. RegCloseKey(hkeyApp);
  212. sizeOfSubKeyName = 255;
  213. continue;
  214. }
  215. if (!RegQueryValueExW(hkeyApp, L"DisplayName", NULL, NULL, NULL, &displen))
  216. {
  217. WCHAR *command;
  218. size = sizeof(value);
  219. if (!RegQueryValueExW(hkeyApp, L"WindowsInstaller", NULL, &type, (BYTE *)&value, &size) &&
  220. type == REG_DWORD && value == 1)
  221. {
  222. command = HeapAlloc(GetProcessHeap(), 0,
  223. (lstrlenW(L"msiexec /x%s") + lstrlenW(subKeyName)) * sizeof(WCHAR));
  224. wsprintfW(command, L"msiexec /x%s", subKeyName);
  225. }
  226. else if (!RegQueryValueExW(hkeyApp, L"UninstallString", NULL, NULL, NULL, &uninstlen))
  227. {
  228. command = HeapAlloc(GetProcessHeap(), 0, uninstlen);
  229. RegQueryValueExW(hkeyApp, L"UninstallString", 0, 0, (BYTE *)command, &uninstlen);
  230. }
  231. else
  232. {
  233. RegCloseKey(hkeyApp);
  234. sizeOfSubKeyName = 255;
  235. continue;
  236. }
  237. numentries++;
  238. entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
  239. entries[numentries-1].root = root;
  240. entries[numentries-1].key = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(subKeyName)+1)*sizeof(WCHAR));
  241. lstrcpyW(entries[numentries-1].key, subKeyName);
  242. entries[numentries-1].descr = HeapAlloc(GetProcessHeap(), 0, displen);
  243. RegQueryValueExW(hkeyApp, L"DisplayName", 0, 0, (BYTE *)entries[numentries-1].descr, &displen);
  244. entries[numentries-1].command = command;
  245. entries[numentries-1].active = 0;
  246. WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
  247. numentries, wine_dbgstr_w(entries[numentries-1].key), wine_dbgstr_w(entries[numentries-1].descr), wine_dbgstr_w(entries[numentries-1].command));
  248. if(sFilter != NULL && StrStrIW(entries[numentries-1].descr,sFilter)==NULL)
  249. numentries--;
  250. }
  251. RegCloseKey(hkeyApp);
  252. sizeOfSubKeyName = 255;
  253. }
  254. return 1;
  255. }
  256. static int FetchUninstallInformation(void)
  257. {
  258. static const BOOL is_64bit = sizeof(void *) > sizeof(int);
  259. int rc = 0;
  260. HKEY root;
  261. numentries = 0;
  262. oldsel = -1;
  263. if (!entries)
  264. entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
  265. if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, PathUninstallW, 0, KEY_READ, &root))
  266. {
  267. rc |= FetchFromRootKey(root);
  268. RegCloseKey(root);
  269. }
  270. if (is_64bit &&
  271. !RegOpenKeyExW(HKEY_LOCAL_MACHINE, PathUninstallW, 0, KEY_READ|KEY_WOW64_32KEY, &root))
  272. {
  273. rc |= FetchFromRootKey(root);
  274. RegCloseKey(root);
  275. }
  276. if (!RegOpenKeyExW(HKEY_CURRENT_USER, PathUninstallW, 0, KEY_READ, &root))
  277. {
  278. rc |= FetchFromRootKey(root);
  279. RegCloseKey(root);
  280. }
  281. qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
  282. return rc;
  283. }
  284. static void UninstallProgram(void)
  285. {
  286. unsigned int i;
  287. WCHAR errormsg[1024];
  288. BOOL res;
  289. STARTUPINFOW si;
  290. PROCESS_INFORMATION info;
  291. DWORD exit_code;
  292. HKEY hkey;
  293. for (i=0; i < numentries; i++)
  294. {
  295. if (!(entries[i].active)) /* don't uninstall this one */
  296. continue;
  297. WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries[i].descr));
  298. memset(&si, 0, sizeof(STARTUPINFOW));
  299. si.cb = sizeof(STARTUPINFOW);
  300. si.wShowWindow = SW_NORMAL;
  301. res = CreateProcessW(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
  302. if (res)
  303. { /* wait for the process to exit */
  304. WaitForSingleObject(info.hProcess, INFINITE);
  305. res = GetExitCodeProcess(info.hProcess, &exit_code);
  306. WINE_TRACE("%d: %08x\n", res, exit_code);
  307. }
  308. else
  309. {
  310. WCHAR sAppName[MAX_STRING_LEN];
  311. WCHAR sUninstallFailed[MAX_STRING_LEN];
  312. HINSTANCE hInst = GetModuleHandleW(0);
  313. LoadStringW(hInst, IDS_APPNAME, sAppName, ARRAY_SIZE(sAppName));
  314. LoadStringW(hInst, IDS_UNINSTALLFAILED, sUninstallFailed, ARRAY_SIZE(sUninstallFailed));
  315. wsprintfW(errormsg, sUninstallFailed, entries[i].command);
  316. if(MessageBoxW(0, errormsg, sAppName, MB_YESNO | MB_ICONQUESTION)==IDYES)
  317. {
  318. /* delete the application's uninstall entry */
  319. RegOpenKeyExW(entries[i].root, PathUninstallW, 0, KEY_READ, &hkey);
  320. RegDeleteKeyW(hkey, entries[i].key);
  321. RegCloseKey(hkey);
  322. }
  323. }
  324. }
  325. WINE_TRACE("finished uninstall phase.\n");
  326. }