2 * Q&D Uninstaller (main.c)
4 * Copyright 2000 Andreas Mohr <andi@lisas.de>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * - add search box for locating entries quickly
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
36 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
38 #define NULL_HANDLE NULL
43 /* use multi-select listbox */
44 #undef USE_MULTIPLESEL
46 /* Delete uninstall registry key after execution.
47 * This is probably a bad idea, because it's the
48 * uninstall program that is supposed to do that.
54 static char about_string[] =
55 "Windows program uninstaller (C) 2000 by Andreas Mohr <andi@lisas.de>";
56 static char program_description[] =
57 "Welcome to the Wine uninstaller !\n\nThe purpose of this program is to let you get rid of all those fantastic programs that somehow manage to always take way too much space on your HDD :-)";
66 uninst_entry *entries = NULL;
69 int list_need_update = 1;
78 { BS_PUSHBUTTON, "Add/Remove", 0 },
79 { BS_PUSHBUTTON, "About", 0 },
80 { BS_PUSHBUTTON, "Exit", 0 }
83 #define NUM (sizeof(button)/sizeof(button[0]))
85 int FetchUninstallInformation(void);
86 void UninstallProgram(void);
88 void ListUninstallPrograms(void)
92 if (! FetchUninstallInformation())
95 for (i=0; i < numentries; i++)
96 printf("%s|||%s\n", entries[i].key, entries[i].descr);
100 void RemoveSpecificProgram(char *name)
104 if (! FetchUninstallInformation())
107 for (i=0; i < numentries; i++)
109 if (strcmp(entries[i].key, name) == 0)
120 fprintf(stderr, "Error: could not match program [%s]\n", name);
125 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
131 /*------------------------------------------------------------------------
132 ** Handle requests just to list the programs
133 **----------------------------------------------------------------------*/
134 if (cmdline && strlen(cmdline) >= 6 && memcmp(cmdline, "--list", 6) == 0)
136 ListUninstallPrograms();
140 /*------------------------------------------------------------------------
141 ** Handle requests to remove one program
142 **----------------------------------------------------------------------*/
143 if (cmdline && strlen(cmdline) > 9 && memcmp(cmdline, "--remove ", 9) == 0)
145 RemoveSpecificProgram(cmdline + 9);
151 LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
154 wc.lpfnWndProc = MainProc;
157 wc.hInstance = hInst;
158 wc.hIcon = LoadIcon( hInst, appname );
159 wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
160 wc.hbrBackground = (HBRUSH) GetStockObject( LTGRAY_BRUSH );
161 wc.lpszMenuName = NULL;
162 wc.lpszClassName = appname;
164 if (!RegisterClass(&wc)) exit(1);
165 hWnd = CreateWindow( appname, appname,
166 WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
167 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
168 NULL_HANDLE, NULL_HANDLE, hInst, NULL );
172 ShowWindow( hWnd, cmdshow );
173 UpdateWindow( hWnd );
175 while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
176 TranslateMessage( &msg );
177 DispatchMessage( &msg );
182 int cmp_by_name(const void *a, const void *b)
184 return strcasecmp(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
187 int FetchUninstallInformation(void)
189 HKEY hkeyUninst, hkeyApp;
191 DWORD sizeOfSubKeyName=255, displen, uninstlen;
192 char subKeyName[256];
199 if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
200 0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
202 MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
207 entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
209 strcpy(key_app, REGSTR_PATH_UNINSTALL);
210 strcat(key_app, "\\");
211 p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
213 RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
214 NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
215 ++i, sizeOfSubKeyName=255 )
217 strcpy(p, subKeyName);
218 RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
220 if ( (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME,
221 0, 0, NULL, &displen) == ERROR_SUCCESS)
222 && (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
223 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
226 entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
227 entries[numentries-1].key =
228 HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
229 strcpy(entries[numentries-1].key, subKeyName);
230 entries[numentries-1].descr =
231 HeapAlloc(GetProcessHeap(), 0, displen);
232 RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, 0,
233 entries[numentries-1].descr, &displen);
234 entries[numentries-1].command =
235 HeapAlloc(GetProcessHeap(), 0, uninstlen);
236 entries[numentries-1].active = 0;
237 RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
238 entries[numentries-1].command, &uninstlen);
239 WINE_TRACE("allocated entry #%d: '%s' ('%s'), '%s'\n", numentries, entries[numentries-1].key, entries[numentries-1].descr, entries[numentries-1].command);
241 RegCloseKey(hkeyApp);
243 qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
244 RegCloseKey(hkeyUninst);
248 void UninstallProgram(void)
254 PROCESS_INFORMATION info;
260 for (i=0; i < numentries; i++)
262 if (!(entries[i].active)) /* don't uninstall this one */
264 WINE_TRACE("uninstalling '%s'\n", entries[i].descr);
265 memset(&si, 0, sizeof(STARTUPINFO));
266 si.cb = sizeof(STARTUPINFO);
267 si.wShowWindow = SW_NORMAL;
268 res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
270 { /* wait for the process to exit */
271 WaitForSingleObject(info.hProcess, INFINITE);
272 res = GetExitCodeProcess(info.hProcess, &exit_code);
273 WINE_TRACE("%d: %08lx\n", res, exit_code);
275 /* delete the program's uninstall entry */
276 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
277 0, KEY_READ, &hkey) == ERROR_SUCCESS)
279 RegDeleteKey(hkey, entries[i].key);
286 sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
287 MessageBox(0, errormsg, appname, MB_OK);
290 WINE_TRACE("finished uninstall phase.\n");
291 list_need_update = 1;
294 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
299 int cxChar, cyChar, i, y, bx, by, maxx, maxy, wx, wy;
300 static HWND hwndList = 0, hwndEdit = 0;
308 GetTextMetrics(hdc, &tm);
309 cxChar = tm.tmAveCharWidth;
310 cyChar = tm.tmHeight + tm.tmExternalLeading;
311 ReleaseDC(hWnd, hdc);
312 /* FIXME: implement sorting and use LBS_SORT here ! */
313 style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
314 #ifdef USE_MULTIPLESEL
315 style |= LBS_MULTIPLESEL;
317 bx = maxx = cxChar * 5;
318 by = maxy = cyChar * 3;
319 hwndList = CreateWindow("listbox", NULL,
322 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 20,
324 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
326 GetWindowRect(hwndList, &rect);
328 maxx += (rect.right - rect.left)*1.1;
329 maxy += (rect.bottom - rect.top)*1.1;
332 for (i=0; i < NUM; i++)
334 button[i].hwnd = CreateWindow("button", button[i].text,
335 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
339 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
344 CreateWindow("static", program_description,
345 WS_CHILD|WS_VISIBLE|SS_LEFT,
349 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
350 maxx += wx + cxChar * 5; /* button + right border */
351 maxy += cyChar * 5 + cyChar * 2; /* static text + distance */
352 CreateWindow("static", "command line to be executed:",
353 WS_CHILD|WS_VISIBLE|SS_LEFT,
357 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
359 hwndEdit = CreateWindow("edit", NULL,
360 WS_CHILD|WS_VISIBLE|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_READONLY,
361 bx, maxy, maxx-(2*bx), (cyChar*6)+4,
363 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
364 maxy += (cyChar*6)+4 + cyChar * 3; /* edit ctrl + bottom border */
365 SetWindowPos( hWnd, 0,
373 hdc = BeginPaint( hWnd, &ps );
374 if (list_need_update)
377 prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
378 if (!(FetchUninstallInformation()))
383 SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
384 SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
385 for (i=0; i < numentries; i++)
387 WINE_TRACE("adding '%s'\n", entries[i].descr);
388 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
390 WINE_TRACE("setting prevsel %d\n", prevsel);
392 SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
393 SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
394 list_need_update = 0;
396 EndPaint( hWnd, &ps );
401 PostQuitMessage( 0 );
405 if ((HWND)lParam == hwndList)
407 if (HIWORD(wParam) == LBN_SELCHANGE)
409 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
411 #ifndef USE_MULTIPLESEL
414 entries[oldsel].active ^= 1; /* toggle */
415 WINE_TRACE("toggling %d old '%s'\n", entries[oldsel].active, entries[oldsel].descr);
418 entries[sel].active ^= 1; /* toggle */
419 WINE_TRACE("toggling %d '%s'\n", entries[sel].active, entries[sel].descr);
420 SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)entries[sel].command);
425 if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
429 InvalidateRect(hWnd, NULL, TRUE);
434 if ((HWND)lParam == button[1].hwnd) /* About button */
435 MessageBox(0, about_string, "About", MB_OK);
437 if ((HWND)lParam == button[2].hwnd) /* Exit button */
442 return( DefWindowProc( hWnd, msg, wParam, lParam ));