Fix signed/unsigned comparison warnings.
[wine] / programs / uninstaller / main.c
1 /*
2  * Q&D Uninstaller (main.c)
3  *
4  * Copyright 2000 Andreas Mohr <andi@lisas.de>
5  * Copyright 2004 Hannu Valtonen <Hannu.Valtonen@hut.fi>
6  *
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.
11  *
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.
16  *
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
20  *
21  * ToDo:
22  * - add search box for locating entries quickly
23  */
24
25
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"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
36
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
43
44 /* use multi-select listbox */
45 #undef USE_MULTIPLESEL
46
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.
50  */
51 #undef DEL_REG_KEY
52
53 char appname[18];
54
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:";
59
60 typedef struct {
61     char *key;
62     WCHAR *descr;
63     char *command;
64     int active;
65 } uninst_entry;
66
67 uninst_entry *entries = NULL;
68
69 unsigned int numentries = 0;
70 int list_need_update = 1;
71 int oldsel = -1;
72
73 struct {
74     DWORD style;
75     LPCSTR text;
76     HWND hwnd;
77 } button[] =
78 {
79     { BS_PUSHBUTTON, "Add/Remove", 0 },
80     { BS_PUSHBUTTON, "About", 0 },
81     { BS_PUSHBUTTON, "Exit", 0 }
82 };
83
84 #define NUM (sizeof(button)/sizeof(button[0]))
85
86 int FetchUninstallInformation(void);
87 void UninstallProgram(void);
88
89 void ListUninstallPrograms(void)
90 {
91     unsigned int i;
92     int len;
93     char *descr;
94
95     if (! FetchUninstallInformation())
96         return;
97
98     for (i=0; i < numentries; i++)
99     {
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);
105     }
106 }
107
108
109 void RemoveSpecificProgram(char *name)
110 {
111     unsigned int i;
112
113     if (! FetchUninstallInformation())
114         return;
115
116     for (i=0; i < numentries; i++)
117     {
118         if (strcmp(entries[i].key, name) == 0)
119         {
120             entries[i].active++;
121             break;
122         }
123     }
124
125     if (i < numentries)
126         UninstallProgram();
127     else
128     {
129         fprintf(stderr, "Error: could not match application [%s]\n", name);
130     }
131 }
132
133 int main( int argc, char *argv[])
134 {
135     MSG msg;
136     WNDCLASS wc;
137     HWND hWnd;
138     LPSTR token = NULL;
139     int i = 1;
140     HINSTANCE hInst = NULL;
141
142     while( i<argc )
143     {
144         token = argv[i++];
145
146         /* Handle requests just to list the applications */
147         if( !lstrcmpA( token, "--list" ) )
148         {
149             ListUninstallPrograms();
150             return 0;
151         }
152         else if( !lstrcmpA( token, "--remove" ) )
153         {
154             if( i >= argc )
155             {
156                 WINE_ERR( "The remove option requires a parameter.\n");
157                 return 1;
158             }
159
160             RemoveSpecificProgram( argv[i++] );
161             return 0;
162         }
163         else 
164         {
165             WINE_ERR( "unknown option %s\n",token);
166             return 1;
167         }
168     }
169
170     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
171
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;
182
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 );
188
189     if (!hWnd) exit(1);
190
191     ShowWindow( hWnd, SW_SHOW );
192     UpdateWindow( hWnd );
193
194     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
195         TranslateMessage( &msg );
196         DispatchMessage( &msg );
197     }
198     return msg.wParam;
199 }
200
201 int cmp_by_name(const void *a, const void *b)
202 {
203     return lstrcmpiW(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
204 }
205
206 int FetchUninstallInformation(void)
207 {
208     HKEY hkeyUninst, hkeyApp;
209     int i;
210     DWORD sizeOfSubKeyName, displen, uninstlen;
211     char subKeyName[256];
212     char key_app[1024];
213     char *p;
214
215     numentries = 0;
216     oldsel = -1;
217     if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
218                             0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
219     {
220         MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
221         return 0;
222     }
223
224     if (!entries)
225         entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
226
227     strcpy(key_app, REGSTR_PATH_UNINSTALL);
228     strcat(key_app, "\\");
229     p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
230
231     sizeOfSubKeyName = 255;
232     for ( i=0;
233           RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
234                          NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
235           ++i )
236     {
237         static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
238
239         strcpy(p, subKeyName);
240         RegOpenKeyExA(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
241
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) )
246         {
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);
265         }
266         RegCloseKey(hkeyApp);
267
268         sizeOfSubKeyName = 255;
269     }
270     qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
271     RegCloseKey(hkeyUninst);
272     return 1;
273 }
274
275 void UninstallProgram(void)
276 {
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
286
287     for (i=0; i < numentries; i++)
288     {
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)
305             {
306                 RegDeleteKey(hkey, entries[i].key);
307                 RegCloseKey(hkey);
308             }
309 #endif
310         }
311         else
312         {
313             sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
314             MessageBox(0, errormsg, appname, MB_OK);
315         }
316     }
317     WINE_TRACE("finished uninstall phase.\n");
318     list_need_update = 1;
319 }
320
321 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
322 {
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;
332
333     switch( msg ) {
334     case WM_CREATE:
335         {
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++)
372         {
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;
382         }
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;
389         }
390
391     case WM_PAINT:
392       {
393         hdc = BeginPaint( hWnd, &ps );
394         if (list_need_update)
395         {
396             int prevsel;
397             prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
398             if (!(FetchUninstallInformation()))
399             {
400                 PostQuitMessage(0);
401                 return 0;
402             }
403             SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
404             SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
405             for (i=0; i < numentries; i++)
406             {
407                 WINE_TRACE("adding %s\n", wine_dbgstr_w(entries[i].descr));
408                 SendMessageW(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
409             }
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;
415         }
416         EndPaint( hWnd, &ps );
417         return 0;
418       }
419
420     case WM_DESTROY:
421         PostQuitMessage( 0 );
422         return 0;
423
424     case WM_COMMAND:
425         if ((HWND)lParam == hwndList)
426         {
427             if (HIWORD(wParam) == LBN_SELCHANGE)
428             {
429                 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
430
431 #ifndef USE_MULTIPLESEL
432                 if (oldsel != -1)
433                 {
434                     entries[oldsel].active ^= 1; /* toggle */
435                     WINE_TRACE("toggling %d old %s\n", entries[oldsel].active,
436                                 wine_dbgstr_w(entries[oldsel].descr));
437                 }
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;
443             }
444         }
445         else
446         if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
447         {
448             UninstallProgram();
449
450             InvalidateRect(hWnd, NULL, TRUE);
451             UpdateWindow(hWnd);
452
453         }
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;
461     }
462
463     return( DefWindowProc( hWnd, msg, wParam, lParam ));
464 }