Don't test i386 types on non i386.
[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 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     int i, len;
92     char *descr;
93
94     if (! FetchUninstallInformation())
95         return;
96
97     for (i=0; i < numentries; i++)
98     {
99         len = WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, NULL, 0, NULL, NULL); 
100         descr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
101         WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, descr, len, NULL, NULL); 
102         printf("%s|||%s\n", entries[i].key, descr);
103         HeapFree(GetProcessHeap(), 0, descr);
104     }
105 }
106
107
108 void RemoveSpecificProgram(char *name)
109 {
110     int i;
111
112     if (! FetchUninstallInformation())
113         return;
114
115     for (i=0; i < numentries; i++)
116     {
117         if (strcmp(entries[i].key, name) == 0)
118         {
119             entries[i].active++;
120             break;
121         }
122     }
123
124     if (i < numentries)
125         UninstallProgram();
126     else
127     {
128         fprintf(stderr, "Error: could not match application [%s]\n", name);
129     }
130 }
131
132 int main( int argc, char *argv[])
133 {
134     MSG msg;
135     WNDCLASS wc;
136     HWND hWnd;
137     LPSTR token = NULL;
138     int i = 1;
139     HINSTANCE hInst = NULL;
140
141     while( i<argc )
142     {
143         token = argv[i++];
144
145         /* Handle requests just to list the applications */
146         if( !lstrcmpA( token, "--list" ) )
147         {
148             ListUninstallPrograms();
149             return 0;
150         }
151         else if( !lstrcmpA( token, "--remove" ) )
152         {
153             if( i >= argc )
154             {
155                 WINE_ERR( "The remove option requires a parameter.\n");
156                 return 1;
157             }
158
159             RemoveSpecificProgram( argv[i++] );
160             return 0;
161         }
162         else 
163         {
164             WINE_ERR( "unknown option %s\n",token);
165             return 1;
166         }
167     }
168
169     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
170
171     wc.style = 0;
172     wc.lpfnWndProc = MainProc;
173     wc.cbClsExtra = 0;
174     wc.cbWndExtra = 0;
175     wc.hInstance = hInst;
176     wc.hIcon = LoadIcon( hInst, appname );
177     wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
178     wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
179     wc.lpszMenuName = NULL;
180     wc.lpszClassName = appname;
181
182     if (!RegisterClass(&wc)) exit(1);
183     hWnd = CreateWindow( appname, "Wine Application Uninstaller",
184         WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
185         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
186         NULL_HANDLE, NULL_HANDLE, hInst, NULL );
187
188     if (!hWnd) exit(1);
189
190     ShowWindow( hWnd, SW_SHOW );
191     UpdateWindow( hWnd );
192
193     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
194         TranslateMessage( &msg );
195         DispatchMessage( &msg );
196     }
197     return msg.wParam;
198 }
199
200 int cmp_by_name(const void *a, const void *b)
201 {
202     return lstrcmpiW(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
203 }
204
205 int FetchUninstallInformation(void)
206 {
207     HKEY hkeyUninst, hkeyApp;
208     int i;
209     DWORD sizeOfSubKeyName, displen, uninstlen;
210     char subKeyName[256];
211     char key_app[1024];
212     char *p;
213
214     numentries = 0;
215     oldsel = -1;
216     if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
217                             0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
218     {
219         MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
220         return 0;
221     }
222
223     if (!entries)
224         entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
225
226     strcpy(key_app, REGSTR_PATH_UNINSTALL);
227     strcat(key_app, "\\");
228     p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
229
230     sizeOfSubKeyName = 255;
231     for ( i=0;
232           RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
233                          NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
234           ++i )
235     {
236         static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
237
238         strcpy(p, subKeyName);
239         RegOpenKeyExA(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
240
241         if ( (RegQueryValueExW(hkeyApp, DisplayNameW,
242                 0, 0, NULL, &displen) == ERROR_SUCCESS)
243         &&   (RegQueryValueExA(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
244                 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
245         {
246             numentries++;
247             entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
248             entries[numentries-1].key =
249                     HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
250             strcpy(entries[numentries-1].key, subKeyName);
251             entries[numentries-1].descr =
252                     HeapAlloc(GetProcessHeap(), 0, displen);
253             RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0,
254                             (LPBYTE)entries[numentries-1].descr, &displen);
255             entries[numentries-1].command =
256                     HeapAlloc(GetProcessHeap(), 0, uninstlen);
257             entries[numentries-1].active = 0;
258             RegQueryValueExA(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
259                             entries[numentries-1].command, &uninstlen);
260             WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
261                 numentries, entries[numentries-1].key,
262                 wine_dbgstr_w(entries[numentries-1].descr),
263                 entries[numentries-1].command);
264         }
265         RegCloseKey(hkeyApp);
266
267         sizeOfSubKeyName = 255;
268     }
269     qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
270     RegCloseKey(hkeyUninst);
271     return 1;
272 }
273
274 void UninstallProgram(void)
275 {
276     int i;
277     char errormsg[1024];
278     BOOL res;
279     STARTUPINFO si;
280     PROCESS_INFORMATION info;
281     DWORD exit_code;
282 #ifdef DEL_REG_KEY
283     HKEY hkey;
284 #endif
285
286     for (i=0; i < numentries; i++)
287     {
288         if (!(entries[i].active)) /* don't uninstall this one */
289             continue;
290         WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries[i].descr));
291         memset(&si, 0, sizeof(STARTUPINFO));
292         si.cb = sizeof(STARTUPINFO);
293         si.wShowWindow = SW_NORMAL;
294         res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
295         if (res == TRUE)
296         {   /* wait for the process to exit */
297             WaitForSingleObject(info.hProcess, INFINITE);
298             res = GetExitCodeProcess(info.hProcess, &exit_code);
299             WINE_TRACE("%d: %08lx\n", res, exit_code);
300 #ifdef DEL_REG_KEY
301             /* delete the application's uninstall entry */
302             if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
303                 0, KEY_READ, &hkey) == ERROR_SUCCESS)
304             {
305                 RegDeleteKey(hkey, entries[i].key);
306                 RegCloseKey(hkey);
307             }
308 #endif
309         }
310         else
311         {
312             sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
313             MessageBox(0, errormsg, appname, MB_OK);
314         }
315     }
316     WINE_TRACE("finished uninstall phase.\n");
317     list_need_update = 1;
318 }
319
320 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
321 {
322     HFONT static_font, listbox_font;
323     HDC hdc;
324     PAINTSTRUCT ps;
325     TEXTMETRIC tm;
326     int cxChar, cyChar, i, y, bx, maxx, maxy, wx, wy;
327     static HWND hwndList = 0, static_text = 0;
328     DWORD style;
329     RECT rect;
330
331     switch( msg ) {
332     case WM_CREATE:
333         {
334         hdc = GetDC(hWnd);
335         GetTextMetrics(hdc, &tm);
336         static_font = CreateFont(tm.tmHeight + tm.tmExternalLeading, 0, 0, 0, 600, FALSE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
337         listbox_font = CreateFont(tm.tmHeight + tm.tmExternalLeading, 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
338         cxChar = tm.tmAveCharWidth;
339         cyChar = tm.tmHeight + tm.tmExternalLeading;
340         ReleaseDC(hWnd, hdc);
341         /* FIXME: implement sorting and use LBS_SORT here ! */
342         style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
343 #ifdef USE_MULTIPLESEL
344         style |= LBS_MULTIPLESEL;
345 #endif
346         bx = maxx = cxChar * 3;
347         y = maxy = cyChar * 1;
348         static_text = CreateWindow("static", program_description,
349                 WS_CHILD|WS_VISIBLE|SS_LEFT,
350                 maxx, maxy,
351                 cxChar * sizeof(program_description), cyChar * 1,
352                 hWnd, (HMENU)1,
353                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
354         SendMessage(static_text, WM_SETFONT, (WPARAM)static_font, MAKELPARAM(FALSE, 0));
355         maxy += cyChar * 2; /*static text + distance */
356         hwndList = CreateWindow("listbox", NULL,
357                 style,
358                 maxx, maxy,
359                 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 10,
360                 hWnd, (HMENU) 1,
361                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
362         SendMessage(hwndList, WM_SETFONT, (WPARAM)listbox_font, MAKELPARAM(FALSE, 0));
363         GetWindowRect(hwndList, &rect);
364         maxx += (rect.right - rect.left)*1.1;   
365         maxy += (rect.bottom - rect.top)*1.1;
366         wx = 20*cxChar;
367         wy = 7*cyChar/4;
368         y = cyChar * 3;
369         for (i=0; i < NUM; i++)
370         {
371             button[i].hwnd = CreateWindow("button", button[i].text,
372                 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
373                 maxx, y,
374                 wx, wy,
375                 hWnd, (HMENU)i,
376                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
377             if (!button[i].hwnd)
378                     PostQuitMessage(0);
379             y += 2*cyChar;
380         }
381         maxx += wx + cxChar * 4;
382         maxy += cyChar * 2; /* window border */
383         SetWindowPos(   hWnd, 0,
384                         0, 0, maxx, maxy,
385                         SWP_NOMOVE);
386         return 0;
387         }
388
389     case WM_PAINT:
390       {
391         hdc = BeginPaint( hWnd, &ps );
392         if (list_need_update)
393         {
394             int prevsel;
395             prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
396             if (!(FetchUninstallInformation()))
397             {
398                 PostQuitMessage(0);
399                 return 0;
400             }
401             SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
402             SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
403             for (i=0; i < numentries; i++)
404             {
405                 WINE_TRACE("adding %s\n", wine_dbgstr_w(entries[i].descr));
406                 SendMessageW(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
407             }
408             WINE_TRACE("setting prevsel %d\n", prevsel);
409             if (prevsel != -1)
410                 SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
411             SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
412             list_need_update = 0;
413         }
414         EndPaint( hWnd, &ps );
415         return 0;
416       }
417
418     case WM_DESTROY:
419         PostQuitMessage( 0 );
420         return 0;
421
422     case WM_COMMAND:
423         if ((HWND)lParam == hwndList)
424         {
425             if (HIWORD(wParam) == LBN_SELCHANGE)
426             {
427                 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
428
429 #ifndef USE_MULTIPLESEL
430                 if (oldsel != -1)
431                 {
432                     entries[oldsel].active ^= 1; /* toggle */
433                     WINE_TRACE("toggling %d old %s\n", entries[oldsel].active,
434                                 wine_dbgstr_w(entries[oldsel].descr));
435                 }
436 #endif
437                 entries[sel].active ^= 1; /* toggle */
438                 WINE_TRACE("toggling %d %s\n", entries[sel].active,
439                             wine_dbgstr_w(entries[oldsel].descr));
440                 oldsel = sel;
441             }
442         }
443         else
444         if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
445         {
446             UninstallProgram();
447
448             InvalidateRect(hWnd, NULL, TRUE);
449             UpdateWindow(hWnd);
450
451         }
452         else
453         if ((HWND)lParam == button[1].hwnd) /* About button */
454             MessageBox(0, about_string, "About Wine Application Uninstaller", MB_OK);
455         else
456         if ((HWND)lParam == button[2].hwnd) /* Exit button */
457             PostQuitMessage(0);
458         return 0;
459     }
460
461     return( DefWindowProc( hWnd, msg, wParam, lParam ));
462 }