Try CreateProcess even if SHGetFileInfo fails so that we can launch
[wine] / programs / uninstaller / main.c
1 /*
2  * Q&D Uninstaller (main.c)
3  *
4  * Copyright 2000 Andreas Mohr <andi@lisas.de>
5  *
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.
10  *
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.
15  *
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
19  *
20  * ToDo:
21  * - add search box for locating entries quickly
22  */
23
24
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"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
35
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
42
43 /* use multi-select listbox */
44 #undef USE_MULTIPLESEL
45
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.
49  */
50 #undef DEL_REG_KEY
51
52 char appname[18];
53
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 :-)";
58
59 typedef struct {
60     char *key;
61     char *descr;
62     char *command;
63     int active;
64 } uninst_entry;
65
66 uninst_entry *entries = NULL;
67
68 int numentries = 0;
69 int list_need_update = 1;
70 int oldsel = -1;
71
72 struct {
73     DWORD style;
74     LPCSTR text;
75     HWND hwnd;
76 } button[] =
77 {
78     { BS_PUSHBUTTON, "Add/Remove", 0 },
79     { BS_PUSHBUTTON, "About", 0 },
80     { BS_PUSHBUTTON, "Exit", 0 }
81 };
82
83 #define NUM (sizeof button/sizeof button[0])
84
85 int FetchUninstallInformation(void);
86 void UninstallProgram(void);
87
88 void ListUninstallPrograms(void)
89 {
90     int i;
91
92     if (! FetchUninstallInformation())
93         return;
94
95     for (i=0; i < numentries; i++)
96         printf("%s|||%s\n", entries[i].key, entries[i].descr);
97 }
98
99
100 void RemoveSpecificProgram(char *name)
101 {
102     int i;
103
104     if (! FetchUninstallInformation())
105         return;
106
107     for (i=0; i < numentries; i++)
108     {
109         if (strcmp(entries[i].key, name) == 0)
110         {
111             entries[i].active++;
112             break;
113         }
114     }
115
116     if (i < numentries)
117         UninstallProgram();
118     else
119     {
120         fprintf(stderr, "Error: could not match program [%s]\n", name);
121     }
122 }
123
124
125 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
126 {
127     MSG msg;
128     WNDCLASS wc;
129     HWND hWnd;
130
131     /*------------------------------------------------------------------------
132     ** Handle requests just to list the programs
133     **----------------------------------------------------------------------*/
134     if (cmdline && strlen(cmdline) >= 6 && memcmp(cmdline, "--list", 6) == 0)
135     {
136         ListUninstallPrograms();
137         return(0);
138     }
139
140     /*------------------------------------------------------------------------
141     ** Handle requests to remove one program
142     **----------------------------------------------------------------------*/
143     if (cmdline && strlen(cmdline) > 9 && memcmp(cmdline, "--remove ", 9) == 0)
144     {
145         RemoveSpecificProgram(cmdline + 9);
146         return(0);
147     }
148
149
150
151     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
152
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;
163
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 );
169
170     if (!hWnd) exit(1);
171
172     ShowWindow( hWnd, cmdshow );
173     UpdateWindow( hWnd );
174
175     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
176         TranslateMessage( &msg );
177         DispatchMessage( &msg );
178     }
179     return msg.wParam;
180 }
181
182 int cmp_by_name(const void *a, const void *b)
183 {
184     return strcasecmp(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
185 }
186
187 int FetchUninstallInformation(void)
188 {
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;
195
196
197     numentries = 0;
198     oldsel = -1;
199     if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
200                             0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
201     {
202         MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
203         return 0;
204     }
205
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 )
213     {
214         strcpy(p, subKeyName);
215         RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
216
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) )
221         {
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);
237         }
238         RegCloseKey(hkeyApp);
239     }
240     qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
241     RegCloseKey(hkeyUninst);
242     return 1;
243 }
244
245 void UninstallProgram(void)
246 {
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
256
257     for (i=0; i < numentries; i++)
258     {
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)
275             {
276                 RegDeleteKey(hkey, entries[i].key);
277                 RegCloseKey(hkey);
278             }
279 #endif
280         }
281         else
282         {
283             sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
284             MessageBox(0, errormsg, appname, MB_OK);
285         }
286     }
287     WINE_TRACE("finished uninstall phase.\n");
288     list_need_update = 1;
289 }
290
291 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
292 {
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;
300
301     switch( msg ) {
302     case WM_CREATE:
303         {
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);
322
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++)
330         {
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;
340         }
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;
366         }
367
368     case WM_PAINT:
369       {
370         hdc = BeginPaint( hWnd, &ps );
371         if (list_need_update)
372         {
373             int prevsel;
374             prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
375             if (!(FetchUninstallInformation()))
376             {
377                 PostQuitMessage(0);
378                 return 0;
379             }
380             SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
381             SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
382             for (i=0; i < numentries; i++)
383             {
384                 WINE_TRACE("adding '%s'\n", entries[i].descr);
385                 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
386             }
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;
392         }
393         EndPaint( hWnd, &ps );
394         return 0;
395       }
396
397     case WM_DESTROY:
398         PostQuitMessage( 0 );
399         return 0;
400
401     case WM_COMMAND:
402         if ((HWND)lParam == hwndList)
403         {
404             if (HIWORD(wParam) == LBN_SELCHANGE)
405             {
406                 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
407
408 #ifndef USE_MULTIPLESEL
409                 if (oldsel != -1)
410                 {
411                     entries[oldsel].active ^= 1; /* toggle */
412                     WINE_TRACE("toggling %d old '%s'\n", entries[oldsel].active, entries[oldsel].descr);
413                 }
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;
419             }
420         }
421         else
422         if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
423         {
424             UninstallProgram();
425
426             InvalidateRect(hWnd, NULL, TRUE);
427             UpdateWindow(hWnd);
428
429         }
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;
437     }
438
439     return( DefWindowProc( hWnd, msg, wParam, lParam ));
440 }