- fix wrong hexadecimal GetLastError() output
[wine] / programs / uninstaller / main.c
1 /*
2  * Q&D Uninstaller (main.c)
3  * 
4  * Copyright 2000 Andreas Mohr <andi@lisas.de>
5  * To be distributed under the Wine License
6  */
7
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <time.h>
13 #include <windows.h>
14 #include "main.h"
15 #include "regstr.h"
16
17 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
18 #ifdef WINE_STRICT
19 #define NULL_HANDLE NULL
20 #else
21 #define NULL_HANDLE 0
22 #endif
23
24 #ifdef DUMB_DEBUG
25 #include <stdio.h>
26 #define DEBUG(x) fprintf(stderr,x)
27 #else
28 #define DEBUG(x) 
29 #endif
30
31 /* use multi-select listbox */
32 #undef USE_MULTIPLESEL
33
34 /* Delete uninstall registry key after execution.
35  * This is probably a bad idea, because it's the
36  * uninstall program that is supposed to do that.
37  */
38 #undef DEL_REG_KEY
39
40 char appname[18];
41
42 static char about_string[] =
43     "Windows program uninstaller (C) 2000 by Andreas Mohr <andi@lisas.de>";
44 static char program_description[] =
45         "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 :-)";
46
47 typedef struct {
48     char *key;
49     char *descr;
50     char *command;
51     int active;
52 } uninst_entry;
53
54 uninst_entry *entries = NULL;
55
56 int numentries = 0;
57
58 struct {
59     DWORD style;
60     LPCSTR text;
61     HWND hwnd;
62 } button[] =
63 {
64     { BS_PUSHBUTTON, "Add/Remove", 0 },
65     { BS_PUSHBUTTON, "About", 0 },
66     { BS_PUSHBUTTON, "Exit", 0 }
67 };
68
69 #define NUM (sizeof button/sizeof button[0])
70
71 int GetUninstallStrings(void);
72 void UninstallProgram(void);
73
74 void ListUninstallPrograms(void)
75 {
76     int i;
77     
78     if (! GetUninstallStrings())
79         exit(1);
80
81     for (i=0; i < numentries; i++)
82         printf("%s|||%s\n", entries[i].key, entries[i].descr);
83 }
84
85
86 void RemoveSpecificProgram(char *name)
87 {
88     int i;
89
90     if (! GetUninstallStrings())
91         exit(1);
92
93     for (i=0; i < numentries; i++)
94     {
95         if (strcmp(entries[i].key, name) == 0)
96         {
97             entries[i].active++;
98             break;
99         }
100     }
101
102     if (i < numentries)
103         UninstallProgram();
104     else
105     {
106         fprintf(stderr, "Error: could not match program [%s]\n", name);
107     }
108 }
109
110
111 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
112
113     MSG msg;
114     WNDCLASS wc;
115     HWND hWnd;
116
117     /*------------------------------------------------------------------------
118     ** Handle requests just to list the programs
119     **----------------------------------------------------------------------*/
120     if (cmdline && strlen(cmdline) >= 6 && memcmp(cmdline, "--list", 6) == 0)
121     {
122         ListUninstallPrograms();
123         return(0);
124     }
125
126     /*------------------------------------------------------------------------
127     ** Handle requests to remove one program
128     **----------------------------------------------------------------------*/
129     if (cmdline && strlen(cmdline) > 9 && memcmp(cmdline, "--remove ", 9) == 0)
130     {
131         RemoveSpecificProgram(cmdline + 9);
132         return(0);
133     }
134
135
136
137     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
138
139     wc.style = 0;
140     wc.lpfnWndProc = MainProc;
141     wc.cbClsExtra = 0;
142     wc.cbWndExtra = 0;
143     wc.hInstance = hInst;
144     wc.hIcon = LoadIcon( hInst, appname );
145     wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
146     wc.hbrBackground = (HBRUSH) GetStockObject( LTGRAY_BRUSH );
147     wc.lpszMenuName = NULL;
148     wc.lpszClassName = appname;
149     
150     if (!RegisterClass(&wc)) exit(1);
151     hWnd = CreateWindow( appname, appname, 
152         WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, 
153         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
154         NULL_HANDLE, NULL_HANDLE, hInst, NULL );
155     
156     if (!hWnd) exit(1);
157     
158     ShowWindow( hWnd, cmdshow );
159     UpdateWindow( hWnd );
160
161     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
162         TranslateMessage( &msg );
163         DispatchMessage( &msg );
164     }
165     return msg.wParam;
166 }
167
168 int GetUninstallStrings(void)
169 {
170     HKEY hkeyUninst, hkeyApp;
171     int i;
172     DWORD sizeOfSubKeyName=255, displen, uninstlen;
173     char   subKeyName[256];
174     char key_app[1024];
175     char *p;
176
177     
178     if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
179                             0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
180     {
181         MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
182         return 0;
183     }
184         
185     strcpy(key_app, REGSTR_PATH_UNINSTALL);
186     strcat(key_app, "\\");
187     p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
188     for ( i=0;
189           RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
190                          NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
191           ++i, sizeOfSubKeyName=255 )
192     {
193         strcpy(p, subKeyName);
194         RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
195
196         if ( (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME,
197                 0, 0, NULL, &displen) == ERROR_SUCCESS)
198         &&   (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
199                 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
200         {
201             numentries++;
202             entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
203             entries[numentries-1].key =
204                     HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
205             strcpy(entries[numentries-1].key, subKeyName);
206             entries[numentries-1].descr =
207                     HeapAlloc(GetProcessHeap(), 0, displen);
208             RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, 0,
209                             entries[numentries-1].descr, &displen);
210             entries[numentries-1].command =
211                     HeapAlloc(GetProcessHeap(), 0, uninstlen);
212             entries[numentries-1].active = 0;
213             RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
214                             entries[numentries-1].command, &uninstlen);
215         }
216         RegCloseKey(hkeyApp);
217     }
218     RegCloseKey(hkeyUninst);
219     return 1;
220 }
221
222 void UninstallProgram(void)
223 {
224     int i;
225     char errormsg[1024];
226     BOOL res;
227     STARTUPINFO si;
228     PROCESS_INFORMATION info;
229     DWORD exit_code;
230 #ifdef DEL_REG_KEY
231     HKEY hkey;
232 #endif
233
234     for (i=0; i < numentries; i++)
235     {
236         if (!(entries[i].active)) /* don't uninstall this one */
237             continue;
238         memset(&si, 0, sizeof(STARTUPINFO));
239         si.cb = sizeof(STARTUPINFO);
240         si.wShowWindow = SW_NORMAL;
241         res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
242         if (res == TRUE)
243         {   /* wait for the process to exit */
244             WaitForSingleObject(info.hProcess, INFINITE);
245             res = GetExitCodeProcess(info.hProcess, &exit_code);
246             fprintf(stderr, "%d: %08lx\n", res, exit_code);
247 #ifdef DEL_REG_KEY
248             /* delete the program's uninstall entry */
249             if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
250                 0, KEY_READ, &hkey) == ERROR_SUCCESS)
251             {
252                 RegDeleteKey(hkey, entries[i].key);
253                 RegCloseKey(hkey);
254             }
255 #endif
256         }
257         else
258         {
259             sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
260             MessageBox(0, errormsg, appname, MB_OK);
261         }
262     }
263 }
264
265 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
266 {
267     HDC hdc;
268     PAINTSTRUCT ps;
269     TEXTMETRIC tm;
270     int cxChar, cyChar, i, y, bx, by, maxx, maxy, wx, wy;
271     static HWND hwndList = 0, hwndEdit = 0;
272     DWORD style;
273     RECT rect;
274
275     switch( msg ) {
276     case WM_CREATE:
277         {
278         if (!(GetUninstallStrings()))
279         {
280             PostQuitMessage(0);
281             return 0;
282         }
283         hdc = GetDC(hWnd);
284         GetTextMetrics(hdc, &tm);
285         cxChar = tm.tmAveCharWidth;
286         cyChar = tm.tmHeight + tm.tmExternalLeading;
287         ReleaseDC(hWnd, hdc);
288         /* FIXME: implement sorting and use LBS_SORT here ! */
289         style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
290 #ifdef USE_MULTIPLESEL
291         style |= LBS_MULTIPLESEL;
292 #endif
293         bx = maxx = cxChar * 5;
294         by = maxy = cyChar * 3;
295         hwndList = CreateWindow("listbox", NULL,
296                 style,
297                 maxx, maxy,
298                 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 20,
299                 hWnd, (HMENU) 1,
300                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
301         
302         GetWindowRect(hwndList, &rect);
303         y = by;
304         maxx += (rect.right - rect.left)*1.1;
305         maxy += (rect.bottom - rect.top)*1.1;
306         wx = 20*cxChar;
307         wy = 7*cyChar/4;
308         for (i=0; i < NUM; i++)
309         {
310             button[i].hwnd = CreateWindow("button", button[i].text,
311                 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
312                 maxx, y,
313                 wx, wy,
314                 hWnd, (HMENU)i,
315                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
316             if (!button[i].hwnd)
317                     PostQuitMessage(0);
318             y += 2*cyChar;
319         }
320         CreateWindow("static", program_description,
321                 WS_CHILD|WS_VISIBLE|SS_LEFT,
322                 bx, maxy,
323                 cxChar * 50, wy,
324                 hWnd, (HMENU)1,
325                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
326         maxx += wx + cxChar * 5; /* button + right border */
327         maxy += cyChar * 5 + cyChar * 2; /* static text + distance */
328         CreateWindow("static", "command line to be executed:",
329                 WS_CHILD|WS_VISIBLE|SS_LEFT,
330                 bx, maxy,
331                 cxChar * 50, cyChar,
332                 hWnd, (HMENU)1,
333                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
334         maxy += cyChar;
335         hwndEdit = CreateWindow("edit", NULL,
336                 WS_CHILD|WS_VISIBLE|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_READONLY,
337                 bx, maxy, maxx-(2*bx), (cyChar*6)+4,
338                 hWnd, (HMENU)1,
339                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
340         maxy += (cyChar*6)+4 + cyChar * 3; /* edit ctrl + bottom border */
341         SetWindowPos(   hWnd, 0,
342                         0, 0, maxx, maxy,
343                         SWP_NOMOVE);
344         return 0;
345         }
346
347     case WM_PAINT:
348       {
349         SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
350         SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
351         for (i=0; i < numentries; i++)
352             SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
353         SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
354         hdc = BeginPaint( hWnd, &ps );
355         EndPaint( hWnd, &ps );
356         return 0;
357       }
358
359     case WM_DESTROY:
360         PostQuitMessage( 0 );
361         return 0;
362     
363     case WM_COMMAND:
364         if ((HWND)lParam == hwndList)
365         {
366             if (HIWORD(wParam) == LBN_SELCHANGE)
367             {
368                 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
369                 
370                 entries[sel].active ^= 1; /* toggle */
371                 SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)entries[sel].command);
372             }
373         }
374         else
375         if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
376         {
377             UninstallProgram();
378    
379             /* update listbox */
380             numentries = 0;
381             GetUninstallStrings();
382             InvalidateRect(hWnd, NULL, TRUE);
383             UpdateWindow(hWnd);
384
385         }
386         else
387         if ((HWND)lParam == button[1].hwnd) /* About button */
388             MessageBox(0, about_string, "About", MB_OK);
389         else
390         if ((HWND)lParam == button[2].hwnd) /* Exit button */
391             PostQuitMessage(0);
392         return 0;
393     }
394         
395     return( DefWindowProc( hWnd, msg, wParam, lParam ));
396 }