Updated the documentation telling to use the
[wine/testsucceed.git] / programs / uninstaller / main.c
blob2898df0aa6d8f1f71cae322c91a0a2646cda8594
1 /*
2 * Q&D Uninstaller (main.c)
4 * Copyright 2000 Andreas Mohr <andi@lisas.de>
5 * Copyright 2004 Hannu Valtonen <Hannu.Valtonen@hut.fi>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * ToDo:
22 * - add search box for locating entries quickly
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <time.h>
30 #include <windows.h>
31 #include "main.h"
32 #include "regstr.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
37 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
38 #ifdef WINE_STRICT
39 #define NULL_HANDLE NULL
40 #else
41 #define NULL_HANDLE 0
42 #endif
44 /* use multi-select listbox */
45 #undef USE_MULTIPLESEL
47 /* Delete uninstall registry key after execution.
48 * This is probably a bad idea, because it's the
49 * uninstall program that is supposed to do that.
51 #undef DEL_REG_KEY
53 char appname[18];
55 static char about_string[] =
56 "Wine Application Uninstaller (C) 2004 by Andreas Mohr <andi@lisas.de> and Hannu Valtonen <Hannu.Valtonen@hut.fi>";
57 static char program_description[] =
58 "Please select the application you wish to uninstall:";
60 typedef struct {
61 char *key;
62 WCHAR *descr;
63 char *command;
64 int active;
65 } uninst_entry;
67 uninst_entry *entries = NULL;
69 unsigned int numentries = 0;
70 int list_need_update = 1;
71 int oldsel = -1;
73 struct {
74 DWORD style;
75 LPCSTR text;
76 HWND hwnd;
77 } button[] =
79 { BS_PUSHBUTTON, "Add/Remove", 0 },
80 { BS_PUSHBUTTON, "About", 0 },
81 { BS_PUSHBUTTON, "Exit", 0 }
84 #define NUM (sizeof(button)/sizeof(button[0]))
86 int FetchUninstallInformation(void);
87 void UninstallProgram(void);
89 void ListUninstallPrograms(void)
91 unsigned int i;
92 int len;
93 char *descr;
95 if (! FetchUninstallInformation())
96 return;
98 for (i=0; i < numentries; i++)
100 len = WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, NULL, 0, NULL, NULL);
101 descr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
102 WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, descr, len, NULL, NULL);
103 printf("%s|||%s\n", entries[i].key, descr);
104 HeapFree(GetProcessHeap(), 0, descr);
109 void RemoveSpecificProgram(char *name)
111 unsigned int i;
113 if (! FetchUninstallInformation())
114 return;
116 for (i=0; i < numentries; i++)
118 if (strcmp(entries[i].key, name) == 0)
120 entries[i].active++;
121 break;
125 if (i < numentries)
126 UninstallProgram();
127 else
129 fprintf(stderr, "Error: could not match application [%s]\n", name);
133 int main( int argc, char *argv[])
135 MSG msg;
136 WNDCLASS wc;
137 HWND hWnd;
138 LPSTR token = NULL;
139 int i = 1;
140 HINSTANCE hInst = NULL;
142 while( i<argc )
144 token = argv[i++];
146 /* Handle requests just to list the applications */
147 if( !lstrcmpA( token, "--list" ) )
149 ListUninstallPrograms();
150 return 0;
152 else if( !lstrcmpA( token, "--remove" ) )
154 if( i >= argc )
156 WINE_ERR( "The remove option requires a parameter.\n");
157 return 1;
160 RemoveSpecificProgram( argv[i++] );
161 return 0;
163 else
165 WINE_ERR( "unknown option %s\n",token);
166 return 1;
170 LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
172 wc.style = 0;
173 wc.lpfnWndProc = MainProc;
174 wc.cbClsExtra = 0;
175 wc.cbWndExtra = 0;
176 wc.hInstance = hInst;
177 wc.hIcon = LoadIcon( hInst, appname );
178 wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
179 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
180 wc.lpszMenuName = NULL;
181 wc.lpszClassName = appname;
183 if (!RegisterClass(&wc)) exit(1);
184 hWnd = CreateWindow( appname, "Wine Application Uninstaller",
185 WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
186 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
187 NULL_HANDLE, NULL_HANDLE, hInst, NULL );
189 if (!hWnd) exit(1);
191 ShowWindow( hWnd, SW_SHOW );
192 UpdateWindow( hWnd );
194 while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
195 TranslateMessage( &msg );
196 DispatchMessage( &msg );
198 return msg.wParam;
201 int cmp_by_name(const void *a, const void *b)
203 return lstrcmpiW(((const uninst_entry *)a)->descr, ((const uninst_entry *)b)->descr);
206 int FetchUninstallInformation(void)
208 HKEY hkeyUninst, hkeyApp;
209 int i;
210 DWORD sizeOfSubKeyName, displen, uninstlen;
211 char subKeyName[256];
212 char key_app[1024];
213 char *p;
215 numentries = 0;
216 oldsel = -1;
217 if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
218 0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
220 MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
221 return 0;
224 if (!entries)
225 entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
227 strcpy(key_app, REGSTR_PATH_UNINSTALL);
228 strcat(key_app, "\\");
229 p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
231 sizeOfSubKeyName = 255;
232 for ( i=0;
233 RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
234 NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
235 ++i )
237 static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
239 strcpy(p, subKeyName);
240 RegOpenKeyExA(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
242 if ( (RegQueryValueExW(hkeyApp, DisplayNameW,
243 0, 0, NULL, &displen) == ERROR_SUCCESS)
244 && (RegQueryValueExA(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
245 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
247 numentries++;
248 entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
249 entries[numentries-1].key =
250 HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
251 strcpy(entries[numentries-1].key, subKeyName);
252 entries[numentries-1].descr =
253 HeapAlloc(GetProcessHeap(), 0, displen);
254 RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0,
255 (LPBYTE)entries[numentries-1].descr, &displen);
256 entries[numentries-1].command =
257 HeapAlloc(GetProcessHeap(), 0, uninstlen);
258 entries[numentries-1].active = 0;
259 RegQueryValueExA(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
260 entries[numentries-1].command, &uninstlen);
261 WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
262 numentries, entries[numentries-1].key,
263 wine_dbgstr_w(entries[numentries-1].descr),
264 entries[numentries-1].command);
266 RegCloseKey(hkeyApp);
268 sizeOfSubKeyName = 255;
270 qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
271 RegCloseKey(hkeyUninst);
272 return 1;
275 void UninstallProgram(void)
277 unsigned int i;
278 char errormsg[1024];
279 BOOL res;
280 STARTUPINFO si;
281 PROCESS_INFORMATION info;
282 DWORD exit_code;
283 #ifdef DEL_REG_KEY
284 HKEY hkey;
285 #endif
287 for (i=0; i < numentries; i++)
289 if (!(entries[i].active)) /* don't uninstall this one */
290 continue;
291 WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries[i].descr));
292 memset(&si, 0, sizeof(STARTUPINFO));
293 si.cb = sizeof(STARTUPINFO);
294 si.wShowWindow = SW_NORMAL;
295 res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
296 if (res == TRUE)
297 { /* wait for the process to exit */
298 WaitForSingleObject(info.hProcess, INFINITE);
299 res = GetExitCodeProcess(info.hProcess, &exit_code);
300 WINE_TRACE("%d: %08lx\n", res, exit_code);
301 #ifdef DEL_REG_KEY
302 /* delete the application's uninstall entry */
303 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
304 0, KEY_READ, &hkey) == ERROR_SUCCESS)
306 RegDeleteKey(hkey, entries[i].key);
307 RegCloseKey(hkey);
309 #endif
311 else
313 sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
314 MessageBox(0, errormsg, appname, MB_OK);
317 WINE_TRACE("finished uninstall phase.\n");
318 list_need_update = 1;
321 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
323 HFONT static_font, listbox_font;
324 HDC hdc;
325 PAINTSTRUCT ps;
326 TEXTMETRIC tm;
327 unsigned int i;
328 int cxChar, cyChar, y, bx, maxx, maxy, wx, wy;
329 static HWND hwndList = 0, static_text = 0;
330 DWORD style;
331 RECT rect;
333 switch( msg ) {
334 case WM_CREATE:
336 hdc = GetDC(hWnd);
337 GetTextMetrics(hdc, &tm);
338 static_font = CreateFont(tm.tmHeight + tm.tmExternalLeading, 0, 0, 0, 600, FALSE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
339 listbox_font = CreateFont(tm.tmHeight + tm.tmExternalLeading, 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
340 cxChar = tm.tmAveCharWidth;
341 cyChar = tm.tmHeight + tm.tmExternalLeading;
342 ReleaseDC(hWnd, hdc);
343 /* FIXME: implement sorting and use LBS_SORT here ! */
344 style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
345 #ifdef USE_MULTIPLESEL
346 style |= LBS_MULTIPLESEL;
347 #endif
348 bx = maxx = cxChar * 3;
349 y = maxy = cyChar * 1;
350 static_text = CreateWindow("static", program_description,
351 WS_CHILD|WS_VISIBLE|SS_LEFT,
352 maxx, maxy,
353 cxChar * sizeof(program_description), cyChar * 1,
354 hWnd, (HMENU)1,
355 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
356 SendMessage(static_text, WM_SETFONT, (WPARAM)static_font, MAKELPARAM(FALSE, 0));
357 maxy += cyChar * 2; /*static text + distance */
358 hwndList = CreateWindow("listbox", NULL,
359 style,
360 maxx, maxy,
361 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 10,
362 hWnd, (HMENU) 1,
363 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
364 SendMessage(hwndList, WM_SETFONT, (WPARAM)listbox_font, MAKELPARAM(FALSE, 0));
365 GetWindowRect(hwndList, &rect);
366 maxx += (rect.right - rect.left)*1.1;
367 maxy += (rect.bottom - rect.top)*1.1;
368 wx = 20*cxChar;
369 wy = 7*cyChar/4;
370 y = cyChar * 3;
371 for (i=0; i < NUM; i++)
373 button[i].hwnd = CreateWindow("button", button[i].text,
374 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
375 maxx, y,
376 wx, wy,
377 hWnd, (HMENU)i,
378 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
379 if (!button[i].hwnd)
380 PostQuitMessage(0);
381 y += 2*cyChar;
383 maxx += wx + cxChar * 4;
384 maxy += cyChar * 2; /* window border */
385 SetWindowPos( hWnd, 0,
386 0, 0, maxx, maxy,
387 SWP_NOMOVE);
388 return 0;
391 case WM_PAINT:
393 hdc = BeginPaint( hWnd, &ps );
394 if (list_need_update)
396 int prevsel;
397 prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
398 if (!(FetchUninstallInformation()))
400 PostQuitMessage(0);
401 return 0;
403 SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
404 SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
405 for (i=0; i < numentries; i++)
407 WINE_TRACE("adding %s\n", wine_dbgstr_w(entries[i].descr));
408 SendMessageW(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
410 WINE_TRACE("setting prevsel %d\n", prevsel);
411 if (prevsel != -1)
412 SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
413 SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
414 list_need_update = 0;
416 EndPaint( hWnd, &ps );
417 return 0;
420 case WM_DESTROY:
421 PostQuitMessage( 0 );
422 return 0;
424 case WM_COMMAND:
425 if ((HWND)lParam == hwndList)
427 if (HIWORD(wParam) == LBN_SELCHANGE)
429 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
431 #ifndef USE_MULTIPLESEL
432 if (oldsel != -1)
434 entries[oldsel].active ^= 1; /* toggle */
435 WINE_TRACE("toggling %d old %s\n", entries[oldsel].active,
436 wine_dbgstr_w(entries[oldsel].descr));
438 #endif
439 entries[sel].active ^= 1; /* toggle */
440 WINE_TRACE("toggling %d %s\n", entries[sel].active,
441 wine_dbgstr_w(entries[oldsel].descr));
442 oldsel = sel;
445 else
446 if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
448 UninstallProgram();
450 InvalidateRect(hWnd, NULL, TRUE);
451 UpdateWindow(hWnd);
454 else
455 if ((HWND)lParam == button[1].hwnd) /* About button */
456 MessageBox(0, about_string, "About Wine Application Uninstaller", MB_OK);
457 else
458 if ((HWND)lParam == button[2].hwnd) /* Exit button */
459 PostQuitMessage(0);
460 return 0;
463 return( DefWindowProc( hWnd, msg, wParam, lParam ));