Don't require write access on the file handle to create a
[wine] / programs / uninstaller / main.c
1 /*
2  * Q&D Uninstaller (main.c)
3  * 
4  * Copyright 2000 Andreas Mohr <a.mohr@mailto.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 <a.mohr@mailto.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 #ifdef USE_MULTIPLESEL
52     int active;
53 #endif
54 } uninst_entry;
55
56 uninst_entry *entries = NULL;
57
58 int numentries = 0;
59
60 int cursel = -1;
61
62 struct {
63     DWORD style;
64     LPCSTR text;
65     HWND hwnd;
66 } button[] =
67 {
68     { BS_PUSHBUTTON, "Add/Remove", 0 },
69     { BS_PUSHBUTTON, "About", 0 },
70     { BS_PUSHBUTTON, "Exit", 0 }
71 };
72
73 #define NUM (sizeof button/sizeof button[0])
74
75 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
76
77     MSG msg;
78     WNDCLASS wc;
79     HWND hWnd;
80
81     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
82
83     wc.style = 0;
84     wc.lpfnWndProc = MainProc;
85     wc.cbClsExtra = 0;
86     wc.cbWndExtra = 0;
87     wc.hInstance = hInst;
88     wc.hIcon = LoadIcon( hInst, appname );
89     wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
90     wc.hbrBackground = (HBRUSH) GetStockObject( LTGRAY_BRUSH );
91     wc.lpszMenuName = NULL;
92     wc.lpszClassName = appname;
93     
94     if (!RegisterClass(&wc)) exit(1);
95     hWnd = CreateWindow( appname, appname, 
96         WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, 
97         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
98         NULL_HANDLE, NULL_HANDLE, hInst, NULL );
99     
100     if (!hWnd) exit(1);
101     
102     ShowWindow( hWnd, cmdshow );
103     UpdateWindow( hWnd );
104
105     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
106         TranslateMessage( &msg );
107         DispatchMessage( &msg );
108     }
109     return msg.wParam;
110 }
111
112 int GetUninstallStrings()
113 {
114     HKEY hkeyUninst, hkeyApp;
115     int i;
116     DWORD sizeOfSubKeyName=255, displen, uninstlen;
117     char   subKeyName[256];
118     char key_app[1024];
119     char *p;
120
121     
122     if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
123                             0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
124     {
125         MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
126         return 0;
127     }
128         
129     strcpy(key_app, REGSTR_PATH_UNINSTALL);
130     strcat(key_app, "\\");
131     p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
132     for ( i=0;
133           RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
134                          NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
135           ++i, sizeOfSubKeyName=255 )
136     {
137         strcpy(p, subKeyName);
138         RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
139
140         if ( (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME,
141                 0, 0, NULL, &displen) == ERROR_SUCCESS)
142         &&   (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
143                 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
144         {
145             numentries++;
146             entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
147             entries[numentries-1].key =
148                     HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
149             strcpy(entries[numentries-1].key, subKeyName);
150             entries[numentries-1].descr =
151                     HeapAlloc(GetProcessHeap(), 0, displen);
152             RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, 0,
153                             entries[numentries-1].descr, &displen);
154             entries[numentries-1].command =
155                     HeapAlloc(GetProcessHeap(), 0, uninstlen);
156 #ifdef USE_MULTIPLESEL
157             entries[numentries-1].active = 0;
158 #endif
159             RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
160                             entries[numentries-1].command, &uninstlen);
161         }
162         RegCloseKey(hkeyApp);
163     }
164     RegCloseKey(hkeyUninst);
165     return 1;
166 }
167
168 void UninstallProgram(HWND hWnd)
169 {
170     int i;
171     char errormsg[1024];
172     BOOL res;
173     STARTUPINFO si;
174     PROCESS_INFORMATION info;
175     DWORD exit_code;
176 #ifdef DEL_REG_KEY
177     HKEY hkey;
178 #endif
179
180 #ifdef USE_MULTIPLESEL
181     for (i=0; i < numentries; i++)
182     {
183         if (!(entries[i].active)) /* don't uninstall this one */
184             continue;
185 #else
186         if (cursel == -1)
187             return;
188         i = cursel;
189 #endif
190         memset(&si, 0, sizeof(STARTUPINFO));
191         si.cb = sizeof(STARTUPINFO);
192         si.wShowWindow = SW_NORMAL;
193         res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
194         if (res == TRUE)
195         {   /* wait for the process to exit */
196             WaitForSingleObject(info.hProcess, INFINITE);
197             res = GetExitCodeProcess(info.hProcess, &exit_code);
198             fprintf(stderr, "%d: %08lx\n", res, exit_code);
199 #ifdef DEL_REG_KEY
200             /* delete the program's uninstall entry */
201             if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
202                 0, KEY_READ, &hkey) == ERROR_SUCCESS)
203             {
204                 RegDeleteKey(hkey, entries[i].key);
205                 RegCloseKey(hkey);
206             }
207 #endif
208    
209             /* update listbox */
210             numentries = 0;
211             GetUninstallStrings();
212             InvalidateRect(hWnd, NULL, TRUE);
213             UpdateWindow(hWnd);
214         }
215         else
216         {
217             sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
218             MessageBox(0, errormsg, appname, MB_OK);
219         }
220 #ifdef USE_MULTIPLESEL
221     }
222 #endif
223 }
224
225 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
226 {
227     HDC hdc;
228     PAINTSTRUCT ps;
229     TEXTMETRIC tm;
230     int cxChar, cyChar, i, y, bx, by, maxx, maxy, wx, wy;
231     static HWND hwndList = 0, hwndEdit = 0;
232     DWORD style;
233     RECT rect;
234
235     switch( msg ) {
236     case WM_CREATE:
237         {
238         if (!(GetUninstallStrings()))
239         {
240             PostQuitMessage(0);
241             return 0;
242         }
243         hdc = GetDC(hWnd);
244         GetTextMetrics(hdc, &tm);
245         cxChar = tm.tmAveCharWidth;
246         cyChar = tm.tmHeight + tm.tmExternalLeading;
247         ReleaseDC(hWnd, hdc);
248         /* FIXME: implement sorting and use LBS_SORT here ! */
249         style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
250 #ifdef USE_MULTIPLESEL
251         style |= LBS_MULTIPLESEL;
252 #endif
253         bx = maxx = cxChar * 5;
254         by = maxy = cyChar * 3;
255         hwndList = CreateWindow("listbox", NULL,
256                 style,
257                 maxx, maxy,
258                 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 20,
259                 hWnd, (HMENU) 1,
260                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
261         
262         GetWindowRect(hwndList, &rect);
263         y = by;
264         maxx += (rect.right - rect.left)*1.1;
265         maxy += (rect.bottom - rect.top)*1.1;
266         wx = 20*cxChar;
267         wy = 7*cyChar/4;
268         for (i=0; i < NUM; i++)
269         {
270             button[i].hwnd = CreateWindow("button", button[i].text,
271                 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
272                 maxx, y,
273                 wx, wy,
274                 hWnd, (HMENU)i,
275                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
276             if (!button[i].hwnd)
277                     PostQuitMessage(0);
278             y += 2*cyChar;
279         }
280         CreateWindow("static", program_description,
281                 WS_CHILD|WS_VISIBLE|SS_LEFT,
282                 bx, maxy,
283                 cxChar * 50, wy,
284                 hWnd, (HMENU)1,
285                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
286         maxx += wx + cxChar * 5; /* button + right border */
287         maxy += cyChar * 5 + cyChar * 2; /* static text + distance */
288         CreateWindow("static", "command line to be executed:",
289                 WS_CHILD|WS_VISIBLE|SS_LEFT,
290                 bx, maxy,
291                 cxChar * 50, cyChar,
292                 hWnd, (HMENU)1,
293                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
294         maxy += cyChar;
295         hwndEdit = CreateWindow("edit", NULL,
296                 WS_CHILD|WS_VISIBLE|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_READONLY,
297                 bx, maxy, maxx-(2*bx), (cyChar*6)+4,
298                 hWnd, (HMENU)1,
299                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
300         maxy += (cyChar*6)+4 + cyChar * 3; /* edit ctrl + bottom border */
301         SetWindowPos(   hWnd, 0,
302                         0, 0, maxx, maxy,
303                         SWP_NOMOVE);
304         return 0;
305         }
306
307     case WM_PAINT:
308       {
309         SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
310         SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
311         for (i=0; i < numentries; i++)
312             SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
313         SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
314         hdc = BeginPaint( hWnd, &ps );
315         EndPaint( hWnd, &ps );
316         return 0;
317       }
318
319     case WM_DESTROY:
320         PostQuitMessage( 0 );
321         return 0;
322     
323     case WM_COMMAND:
324         if ((HWND)lParam == hwndList)
325         {
326             if (HIWORD(wParam) == LBN_SELCHANGE)
327             {
328                 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
329                 
330 #ifdef USE_MULTIPLESEL
331                 entries[sel].active ^= 1; /* toggle */
332 #else
333                 cursel = sel;
334 #endif
335                 SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)entries[sel].command);
336             }
337         }
338         else
339         if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
340             UninstallProgram(hWnd);
341         else
342         if ((HWND)lParam == button[1].hwnd) /* About button */
343             MessageBox(0, about_string, "About", MB_OK);
344         else
345         if ((HWND)lParam == button[2].hwnd) /* Exit button */
346             PostQuitMessage(0);
347         return 0;
348     }
349         
350     return( DefWindowProc( hWnd, msg, wParam, lParam ));
351 }