2 * Q&D Uninstaller (main.c)
4 * Copyright 2000 Andreas Mohr <andi@lisas.de>
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.
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.
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
21 * - add search box for locating entries quickly
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller
);
36 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
38 #define NULL_HANDLE NULL
43 /* use multi-select listbox */
44 #undef USE_MULTIPLESEL
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.
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 :-)";
66 uninst_entry
*entries
= NULL
;
69 int list_need_update
= 1;
78 { BS_PUSHBUTTON
, "Add/Remove", 0 },
79 { BS_PUSHBUTTON
, "About", 0 },
80 { BS_PUSHBUTTON
, "Exit", 0 }
83 #define NUM (sizeof button/sizeof button[0])
85 int FetchUninstallInformation(void);
86 void UninstallProgram(void);
88 void ListUninstallPrograms(void)
92 if (! FetchUninstallInformation())
95 for (i
=0; i
< numentries
; i
++)
96 printf("%s|||%s\n", entries
[i
].key
, entries
[i
].descr
);
100 void RemoveSpecificProgram(char *name
)
104 if (! FetchUninstallInformation())
107 for (i
=0; i
< numentries
; i
++)
109 if (strcmp(entries
[i
].key
, name
) == 0)
120 fprintf(stderr
, "Error: could not match program [%s]\n", name
);
125 int WINAPI
WinMain( HINSTANCE hInst
, HINSTANCE hPrevInst
, LPSTR cmdline
, int cmdshow
)
131 /*------------------------------------------------------------------------
132 ** Handle requests just to list the programs
133 **----------------------------------------------------------------------*/
134 if (cmdline
&& strlen(cmdline
) >= 6 && memcmp(cmdline
, "--list", 6) == 0)
136 ListUninstallPrograms();
140 /*------------------------------------------------------------------------
141 ** Handle requests to remove one program
142 **----------------------------------------------------------------------*/
143 if (cmdline
&& strlen(cmdline
) > 9 && memcmp(cmdline
, "--remove ", 9) == 0)
145 RemoveSpecificProgram(cmdline
+ 9);
151 LoadString( hInst
, IDS_APPNAME
, appname
, sizeof(appname
));
154 wc
.lpfnWndProc
= MainProc
;
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
;
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
);
172 ShowWindow( hWnd
, cmdshow
);
173 UpdateWindow( hWnd
);
175 while( GetMessage(&msg
, NULL_HANDLE
, 0, 0) ) {
176 TranslateMessage( &msg
);
177 DispatchMessage( &msg
);
182 int cmp_by_name(const void *a
, const void *b
)
184 return strcasecmp(((uninst_entry
*)a
)->descr
, ((uninst_entry
*)b
)->descr
);
187 int FetchUninstallInformation(void)
189 HKEY hkeyUninst
, hkeyApp
;
191 DWORD sizeOfSubKeyName
=255, displen
, uninstlen
;
192 char subKeyName
[256];
199 if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REGSTR_PATH_UNINSTALL
,
200 0, KEY_READ
, &hkeyUninst
) != ERROR_SUCCESS
)
202 MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname
, MB_OK
);
206 strcpy(key_app
, REGSTR_PATH_UNINSTALL
);
207 strcat(key_app
, "\\");
208 p
= key_app
+strlen(REGSTR_PATH_UNINSTALL
)+1;
210 RegEnumKeyExA( hkeyUninst
, i
, subKeyName
, &sizeOfSubKeyName
,
211 NULL
, NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
;
212 ++i
, sizeOfSubKeyName
=255 )
214 strcpy(p
, subKeyName
);
215 RegOpenKeyEx(HKEY_LOCAL_MACHINE
, key_app
, 0, KEY_READ
, &hkeyApp
);
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
) )
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
);
238 RegCloseKey(hkeyApp
);
240 qsort(entries
, numentries
, sizeof(uninst_entry
), cmp_by_name
);
241 RegCloseKey(hkeyUninst
);
245 void UninstallProgram(void)
251 PROCESS_INFORMATION info
;
257 for (i
=0; i
< numentries
; i
++)
259 if (!(entries
[i
].active
)) /* don't uninstall this one */
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
);
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
);
272 /* delete the program's uninstall entry */
273 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REGSTR_PATH_UNINSTALL
,
274 0, KEY_READ
, &hkey
) == ERROR_SUCCESS
)
276 RegDeleteKey(hkey
, entries
[i
].key
);
283 sprintf(errormsg
, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries
[i
].command
);
284 MessageBox(0, errormsg
, appname
, MB_OK
);
287 WINE_TRACE("finished uninstall phase.\n");
288 list_need_update
= 1;
291 LRESULT WINAPI
MainProc( HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
296 int cxChar
, cyChar
, i
, y
, bx
, by
, maxx
, maxy
, wx
, wy
;
297 static HWND hwndList
= 0, hwndEdit
= 0;
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
;
314 bx
= maxx
= cxChar
* 5;
315 by
= maxy
= cyChar
* 3;
316 hwndList
= CreateWindow("listbox", NULL
,
319 cxChar
* 50 + GetSystemMetrics(SM_CXVSCROLL
), cyChar
* 20,
321 (HINSTANCE
)GetWindowLong(hWnd
, GWL_HINSTANCE
), NULL
);
323 GetWindowRect(hwndList
, &rect
);
325 maxx
+= (rect
.right
- rect
.left
)*1.1;
326 maxy
+= (rect
.bottom
- rect
.top
)*1.1;
329 for (i
=0; i
< NUM
; i
++)
331 button
[i
].hwnd
= CreateWindow("button", button
[i
].text
,
332 WS_CHILD
|WS_VISIBLE
|BS_PUSHBUTTON
,
336 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
341 CreateWindow("static", program_description
,
342 WS_CHILD
|WS_VISIBLE
|SS_LEFT
,
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
,
354 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
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,
360 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
361 maxy
+= (cyChar
*6)+4 + cyChar
* 3; /* edit ctrl + bottom border */
362 SetWindowPos( hWnd
, 0,
370 hdc
= BeginPaint( hWnd
, &ps
);
371 if (list_need_update
)
374 prevsel
= SendMessage(hwndList
, LB_GETCURSEL
, 0, 0);
375 if (!(FetchUninstallInformation()))
380 SendMessage(hwndList
, LB_RESETCONTENT
, 0, 0);
381 SendMessage(hwndList
, WM_SETREDRAW
, FALSE
, 0);
382 for (i
=0; i
< numentries
; i
++)
384 WINE_TRACE("adding '%s'\n", entries
[i
].descr
);
385 SendMessage(hwndList
, LB_ADDSTRING
, 0, (LPARAM
)entries
[i
].descr
);
387 WINE_TRACE("setting prevsel %d\n", prevsel
);
389 SendMessage(hwndList
, LB_SETCURSEL
, prevsel
, 0 );
390 SendMessage(hwndList
, WM_SETREDRAW
, TRUE
, 0);
391 list_need_update
= 0;
393 EndPaint( hWnd
, &ps
);
398 PostQuitMessage( 0 );
402 if ((HWND
)lParam
== hwndList
)
404 if (HIWORD(wParam
) == LBN_SELCHANGE
)
406 int sel
= SendMessage(hwndList
, LB_GETCURSEL
, 0, 0);
408 #ifndef USE_MULTIPLESEL
411 entries
[oldsel
].active
^= 1; /* toggle */
412 WINE_TRACE("toggling %d old '%s'\n", entries
[oldsel
].active
, entries
[oldsel
].descr
);
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
);
422 if ((HWND
)lParam
== button
[0].hwnd
) /* Uninstall button */
426 InvalidateRect(hWnd
, NULL
, TRUE
);
431 if ((HWND
)lParam
== button
[1].hwnd
) /* About button */
432 MessageBox(0, about_string
, "About", MB_OK
);
434 if ((HWND
)lParam
== button
[2].hwnd
) /* Exit button */
439 return( DefWindowProc( hWnd
, msg
, wParam
, lParam
));