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
33 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
35 #define NULL_HANDLE NULL
43 #define DEBUG(x...) fprintf(stderr,x)
48 /* use multi-select listbox */
49 #undef USE_MULTIPLESEL
51 /* Delete uninstall registry key after execution.
52 * This is probably a bad idea, because it's the
53 * uninstall program that is supposed to do that.
59 static char about_string[] =
60 "Windows program uninstaller (C) 2000 by Andreas Mohr <andi@lisas.de>";
61 static char program_description[] =
62 "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 :-)";
71 uninst_entry *entries = NULL;
74 int list_need_update = 1;
83 { BS_PUSHBUTTON, "Add/Remove", 0 },
84 { BS_PUSHBUTTON, "About", 0 },
85 { BS_PUSHBUTTON, "Exit", 0 }
88 #define NUM (sizeof button/sizeof button[0])
90 int FetchUninstallInformation(void);
91 void UninstallProgram(void);
93 void ListUninstallPrograms(void)
97 if (! FetchUninstallInformation())
100 for (i=0; i < numentries; i++)
101 printf("%s|||%s\n", entries[i].key, entries[i].descr);
105 void RemoveSpecificProgram(char *name)
109 if (! FetchUninstallInformation())
112 for (i=0; i < numentries; i++)
114 if (strcmp(entries[i].key, name) == 0)
125 fprintf(stderr, "Error: could not match program [%s]\n", name);
130 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
136 /*------------------------------------------------------------------------
137 ** Handle requests just to list the programs
138 **----------------------------------------------------------------------*/
139 if (cmdline && strlen(cmdline) >= 6 && memcmp(cmdline, "--list", 6) == 0)
141 ListUninstallPrograms();
145 /*------------------------------------------------------------------------
146 ** Handle requests to remove one program
147 **----------------------------------------------------------------------*/
148 if (cmdline && strlen(cmdline) > 9 && memcmp(cmdline, "--remove ", 9) == 0)
150 RemoveSpecificProgram(cmdline + 9);
156 LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
159 wc.lpfnWndProc = MainProc;
162 wc.hInstance = hInst;
163 wc.hIcon = LoadIcon( hInst, appname );
164 wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
165 wc.hbrBackground = (HBRUSH) GetStockObject( LTGRAY_BRUSH );
166 wc.lpszMenuName = NULL;
167 wc.lpszClassName = appname;
169 if (!RegisterClass(&wc)) exit(1);
170 hWnd = CreateWindow( appname, appname,
171 WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
172 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
173 NULL_HANDLE, NULL_HANDLE, hInst, NULL );
177 ShowWindow( hWnd, cmdshow );
178 UpdateWindow( hWnd );
180 while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
181 TranslateMessage( &msg );
182 DispatchMessage( &msg );
187 int cmp_by_name(const void *a, const void *b)
189 return strcasecmp(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
192 int FetchUninstallInformation(void)
194 HKEY hkeyUninst, hkeyApp;
196 DWORD sizeOfSubKeyName=255, displen, uninstlen;
197 char subKeyName[256];
204 if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
205 0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
207 MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
211 strcpy(key_app, REGSTR_PATH_UNINSTALL);
212 strcat(key_app, "\\");
213 p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
215 RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
216 NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
217 ++i, sizeOfSubKeyName=255 )
219 strcpy(p, subKeyName);
220 RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
222 if ( (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME,
223 0, 0, NULL, &displen) == ERROR_SUCCESS)
224 && (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
225 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
228 entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
229 entries[numentries-1].key =
230 HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
231 strcpy(entries[numentries-1].key, subKeyName);
232 entries[numentries-1].descr =
233 HeapAlloc(GetProcessHeap(), 0, displen);
234 RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, 0,
235 entries[numentries-1].descr, &displen);
236 entries[numentries-1].command =
237 HeapAlloc(GetProcessHeap(), 0, uninstlen);
238 entries[numentries-1].active = 0;
239 RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
240 entries[numentries-1].command, &uninstlen);
241 DEBUG("allocated entry #%d: '%s' ('%s'), '%s'\n", numentries, entries[numentries-1].key, entries[numentries-1].descr, entries[numentries-1].command);
243 RegCloseKey(hkeyApp);
245 qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
246 RegCloseKey(hkeyUninst);
250 void UninstallProgram(void)
256 PROCESS_INFORMATION info;
262 for (i=0; i < numentries; i++)
264 if (!(entries[i].active)) /* don't uninstall this one */
266 DEBUG("uninstalling '%s'\n", entries[i].descr);
267 memset(&si, 0, sizeof(STARTUPINFO));
268 si.cb = sizeof(STARTUPINFO);
269 si.wShowWindow = SW_NORMAL;
270 res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
272 { /* wait for the process to exit */
273 WaitForSingleObject(info.hProcess, INFINITE);
274 res = GetExitCodeProcess(info.hProcess, &exit_code);
275 DEBUG("%d: %08lx\n", res, exit_code);
277 /* delete the program's uninstall entry */
278 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
279 0, KEY_READ, &hkey) == ERROR_SUCCESS)
281 RegDeleteKey(hkey, entries[i].key);
288 sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
289 MessageBox(0, errormsg, appname, MB_OK);
292 DEBUG("finished uninstall phase.\n");
293 list_need_update = 1;
296 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
301 int cxChar, cyChar, i, y, bx, by, maxx, maxy, wx, wy;
302 static HWND hwndList = 0, hwndEdit = 0;
310 GetTextMetrics(hdc, &tm);
311 cxChar = tm.tmAveCharWidth;
312 cyChar = tm.tmHeight + tm.tmExternalLeading;
313 ReleaseDC(hWnd, hdc);
314 /* FIXME: implement sorting and use LBS_SORT here ! */
315 style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
316 #ifdef USE_MULTIPLESEL
317 style |= LBS_MULTIPLESEL;
319 bx = maxx = cxChar * 5;
320 by = maxy = cyChar * 3;
321 hwndList = CreateWindow("listbox", NULL,
324 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 20,
326 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
328 GetWindowRect(hwndList, &rect);
330 maxx += (rect.right - rect.left)*1.1;
331 maxy += (rect.bottom - rect.top)*1.1;
334 for (i=0; i < NUM; i++)
336 button[i].hwnd = CreateWindow("button", button[i].text,
337 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
341 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
346 CreateWindow("static", program_description,
347 WS_CHILD|WS_VISIBLE|SS_LEFT,
351 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
352 maxx += wx + cxChar * 5; /* button + right border */
353 maxy += cyChar * 5 + cyChar * 2; /* static text + distance */
354 CreateWindow("static", "command line to be executed:",
355 WS_CHILD|WS_VISIBLE|SS_LEFT,
359 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
361 hwndEdit = CreateWindow("edit", NULL,
362 WS_CHILD|WS_VISIBLE|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_READONLY,
363 bx, maxy, maxx-(2*bx), (cyChar*6)+4,
365 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
366 maxy += (cyChar*6)+4 + cyChar * 3; /* edit ctrl + bottom border */
367 SetWindowPos( hWnd, 0,
375 if (list_need_update)
378 prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
379 if (!(FetchUninstallInformation()))
384 SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
385 SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
386 for (i=0; i < numentries; i++)
388 DEBUG("adding '%s'\n", entries[i].descr);
389 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
391 DEBUG("setting prevsel %d\n", prevsel);
393 SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
394 SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
395 list_need_update = 0;
397 hdc = BeginPaint( hWnd, &ps );
398 EndPaint( hWnd, &ps );
403 PostQuitMessage( 0 );
407 if ((HWND)lParam == hwndList)
409 if (HIWORD(wParam) == LBN_SELCHANGE)
411 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
413 #ifndef USE_MULTIPLESEL
416 entries[oldsel].active ^= 1; /* toggle */
417 DEBUG("toggling %d old '%s'\n", entries[oldsel].active, entries[oldsel].descr);
420 entries[sel].active ^= 1; /* toggle */
421 DEBUG("toggling %d '%s'\n", entries[sel].active, entries[sel].descr);
422 SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)entries[sel].command);
427 if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
431 InvalidateRect(hWnd, NULL, TRUE);
436 if ((HWND)lParam == button[1].hwnd) /* About button */
437 MessageBox(0, about_string, "About", MB_OK);
439 if ((HWND)lParam == button[2].hwnd) /* Exit button */
444 return( DefWindowProc( hWnd, msg, wParam, lParam ));