Remove ERR() if menu entry isn't of type MF_STRING as some
[wine.git] / programs / uninstaller / main.c
blob365ba671b63eb3e95388b8e1b1c73c8fe2635288
1 /*
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
20 * ToDo:
21 * - add search box for locating entries quickly
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29 #include <windows.h>
30 #include "main.h"
31 #include "regstr.h"
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 */
37 #ifdef WINE_STRICT
38 #define NULL_HANDLE NULL
39 #else
40 #define NULL_HANDLE 0
41 #endif
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.
50 #undef DEL_REG_KEY
52 char appname[18];
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 :-)";
59 typedef struct {
60 char *key;
61 char *descr;
62 char *command;
63 int active;
64 } uninst_entry;
66 uninst_entry *entries = NULL;
68 int numentries = 0;
69 int list_need_update = 1;
70 int oldsel = -1;
72 struct {
73 DWORD style;
74 LPCSTR text;
75 HWND hwnd;
76 } button[] =
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)
90 int i;
92 if (! FetchUninstallInformation())
93 return;
95 for (i=0; i < numentries; i++)
96 printf("%s|||%s\n", entries[i].key, entries[i].descr);
100 void RemoveSpecificProgram(char *name)
102 int i;
104 if (! FetchUninstallInformation())
105 return;
107 for (i=0; i < numentries; i++)
109 if (strcmp(entries[i].key, name) == 0)
111 entries[i].active++;
112 break;
116 if (i < numentries)
117 UninstallProgram();
118 else
120 fprintf(stderr, "Error: could not match program [%s]\n", name);
125 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
127 MSG msg;
128 WNDCLASS wc;
129 HWND hWnd;
131 /*------------------------------------------------------------------------
132 ** Handle requests just to list the programs
133 **----------------------------------------------------------------------*/
134 if (cmdline && strlen(cmdline) >= 6 && memcmp(cmdline, "--list", 6) == 0)
136 ListUninstallPrograms();
137 return(0);
140 /*------------------------------------------------------------------------
141 ** Handle requests to remove one program
142 **----------------------------------------------------------------------*/
143 if (cmdline && strlen(cmdline) > 9 && memcmp(cmdline, "--remove ", 9) == 0)
145 RemoveSpecificProgram(cmdline + 9);
146 return(0);
151 LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
153 wc.style = 0;
154 wc.lpfnWndProc = MainProc;
155 wc.cbClsExtra = 0;
156 wc.cbWndExtra = 0;
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 );
170 if (!hWnd) exit(1);
172 ShowWindow( hWnd, cmdshow );
173 UpdateWindow( hWnd );
175 while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
176 TranslateMessage( &msg );
177 DispatchMessage( &msg );
179 return msg.wParam;
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;
190 int i;
191 DWORD sizeOfSubKeyName=255, displen, uninstlen;
192 char subKeyName[256];
193 char key_app[1024];
194 char *p;
197 numentries = 0;
198 oldsel = -1;
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);
203 return 0;
206 strcpy(key_app, REGSTR_PATH_UNINSTALL);
207 strcat(key_app, "\\");
208 p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
209 for ( i=0;
210 RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
211 NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
212 ++i, sizeOfSubKeyName=255 )
214 strcpy(p, subKeyName);
215 RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
217 if ( (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME,
218 0, 0, NULL, &displen) == ERROR_SUCCESS)
219 && (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
220 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
222 numentries++;
223 entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
224 entries[numentries-1].key =
225 HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
226 strcpy(entries[numentries-1].key, subKeyName);
227 entries[numentries-1].descr =
228 HeapAlloc(GetProcessHeap(), 0, displen);
229 RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, 0,
230 entries[numentries-1].descr, &displen);
231 entries[numentries-1].command =
232 HeapAlloc(GetProcessHeap(), 0, uninstlen);
233 entries[numentries-1].active = 0;
234 RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
235 entries[numentries-1].command, &uninstlen);
236 WINE_TRACE("allocated entry #%d: '%s' ('%s'), '%s'\n", numentries, entries[numentries-1].key, entries[numentries-1].descr, entries[numentries-1].command);
238 RegCloseKey(hkeyApp);
240 qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
241 RegCloseKey(hkeyUninst);
242 return 1;
245 void UninstallProgram(void)
247 int i;
248 char errormsg[1024];
249 BOOL res;
250 STARTUPINFO si;
251 PROCESS_INFORMATION info;
252 DWORD exit_code;
253 #ifdef DEL_REG_KEY
254 HKEY hkey;
255 #endif
257 for (i=0; i < numentries; i++)
259 if (!(entries[i].active)) /* don't uninstall this one */
260 continue;
261 WINE_TRACE("uninstalling '%s'\n", entries[i].descr);
262 memset(&si, 0, sizeof(STARTUPINFO));
263 si.cb = sizeof(STARTUPINFO);
264 si.wShowWindow = SW_NORMAL;
265 res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
266 if (res == TRUE)
267 { /* wait for the process to exit */
268 WaitForSingleObject(info.hProcess, INFINITE);
269 res = GetExitCodeProcess(info.hProcess, &exit_code);
270 WINE_TRACE("%d: %08lx\n", res, exit_code);
271 #ifdef DEL_REG_KEY
272 /* delete the program's uninstall entry */
273 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
274 0, KEY_READ, &hkey) == ERROR_SUCCESS)
276 RegDeleteKey(hkey, entries[i].key);
277 RegCloseKey(hkey);
279 #endif
281 else
283 sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
284 MessageBox(0, errormsg, appname, MB_OK);
287 WINE_TRACE("finished uninstall phase.\n");
288 list_need_update = 1;
291 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
293 HDC hdc;
294 PAINTSTRUCT ps;
295 TEXTMETRIC tm;
296 int cxChar, cyChar, i, y, bx, by, maxx, maxy, wx, wy;
297 static HWND hwndList = 0, hwndEdit = 0;
298 DWORD style;
299 RECT rect;
301 switch( msg ) {
302 case WM_CREATE:
304 hdc = GetDC(hWnd);
305 GetTextMetrics(hdc, &tm);
306 cxChar = tm.tmAveCharWidth;
307 cyChar = tm.tmHeight + tm.tmExternalLeading;
308 ReleaseDC(hWnd, hdc);
309 /* FIXME: implement sorting and use LBS_SORT here ! */
310 style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
311 #ifdef USE_MULTIPLESEL
312 style |= LBS_MULTIPLESEL;
313 #endif
314 bx = maxx = cxChar * 5;
315 by = maxy = cyChar * 3;
316 hwndList = CreateWindow("listbox", NULL,
317 style,
318 maxx, maxy,
319 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 20,
320 hWnd, (HMENU) 1,
321 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
323 GetWindowRect(hwndList, &rect);
324 y = by;
325 maxx += (rect.right - rect.left)*1.1;
326 maxy += (rect.bottom - rect.top)*1.1;
327 wx = 20*cxChar;
328 wy = 7*cyChar/4;
329 for (i=0; i < NUM; i++)
331 button[i].hwnd = CreateWindow("button", button[i].text,
332 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
333 maxx, y,
334 wx, wy,
335 hWnd, (HMENU)i,
336 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
337 if (!button[i].hwnd)
338 PostQuitMessage(0);
339 y += 2*cyChar;
341 CreateWindow("static", program_description,
342 WS_CHILD|WS_VISIBLE|SS_LEFT,
343 bx, maxy,
344 cxChar * 50, wy,
345 hWnd, (HMENU)1,
346 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
347 maxx += wx + cxChar * 5; /* button + right border */
348 maxy += cyChar * 5 + cyChar * 2; /* static text + distance */
349 CreateWindow("static", "command line to be executed:",
350 WS_CHILD|WS_VISIBLE|SS_LEFT,
351 bx, maxy,
352 cxChar * 50, cyChar,
353 hWnd, (HMENU)1,
354 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
355 maxy += cyChar;
356 hwndEdit = CreateWindow("edit", NULL,
357 WS_CHILD|WS_VISIBLE|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_READONLY,
358 bx, maxy, maxx-(2*bx), (cyChar*6)+4,
359 hWnd, (HMENU)1,
360 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
361 maxy += (cyChar*6)+4 + cyChar * 3; /* edit ctrl + bottom border */
362 SetWindowPos( hWnd, 0,
363 0, 0, maxx, maxy,
364 SWP_NOMOVE);
365 return 0;
368 case WM_PAINT:
370 hdc = BeginPaint( hWnd, &ps );
371 if (list_need_update)
373 int prevsel;
374 prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
375 if (!(FetchUninstallInformation()))
377 PostQuitMessage(0);
378 return 0;
380 SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
381 SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
382 for (i=0; i < numentries; i++)
384 WINE_TRACE("adding '%s'\n", entries[i].descr);
385 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
387 WINE_TRACE("setting prevsel %d\n", prevsel);
388 if (prevsel != -1)
389 SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
390 SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
391 list_need_update = 0;
393 EndPaint( hWnd, &ps );
394 return 0;
397 case WM_DESTROY:
398 PostQuitMessage( 0 );
399 return 0;
401 case WM_COMMAND:
402 if ((HWND)lParam == hwndList)
404 if (HIWORD(wParam) == LBN_SELCHANGE)
406 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
408 #ifndef USE_MULTIPLESEL
409 if (oldsel != -1)
411 entries[oldsel].active ^= 1; /* toggle */
412 WINE_TRACE("toggling %d old '%s'\n", entries[oldsel].active, entries[oldsel].descr);
414 #endif
415 entries[sel].active ^= 1; /* toggle */
416 WINE_TRACE("toggling %d '%s'\n", entries[sel].active, entries[sel].descr);
417 SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)entries[sel].command);
418 oldsel = sel;
421 else
422 if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
424 UninstallProgram();
426 InvalidateRect(hWnd, NULL, TRUE);
427 UpdateWindow(hWnd);
430 else
431 if ((HWND)lParam == button[1].hwnd) /* About button */
432 MessageBox(0, about_string, "About", MB_OK);
433 else
434 if ((HWND)lParam == button[2].hwnd) /* Exit button */
435 PostQuitMessage(0);
436 return 0;
439 return( DefWindowProc( hWnd, msg, wParam, lParam ));