2 * Q&D Uninstaller (main.c)
4 * Copyright 2000 Andreas Mohr <andi@lisas.de>
5 * Copyright 2004 Hannu Valtonen <Hannu.Valtonen@hut.fi>
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.
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.
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
22 * - add search box for locating entries quickly
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller
);
37 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
39 #define NULL_HANDLE NULL
44 /* use multi-select listbox */
45 #undef USE_MULTIPLESEL
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.
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:";
67 uninst_entry
*entries
= NULL
;
69 unsigned int numentries
= 0;
70 int list_need_update
= 1;
79 { BS_PUSHBUTTON
, "Add/Remove", 0 },
80 { BS_PUSHBUTTON
, "About", 0 },
81 { BS_PUSHBUTTON
, "Exit", 0 }
84 #define NUM (sizeof(button)/sizeof(button[0]))
86 int FetchUninstallInformation(void);
87 void UninstallProgram(void);
89 void ListUninstallPrograms(void)
95 if (! FetchUninstallInformation())
98 for (i
=0; i
< numentries
; i
++)
100 len
= WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].descr
, -1, NULL
, 0, NULL
, NULL
);
101 descr
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
102 WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].descr
, -1, descr
, len
, NULL
, NULL
);
103 printf("%s|||%s\n", entries
[i
].key
, descr
);
104 HeapFree(GetProcessHeap(), 0, descr
);
109 void RemoveSpecificProgram(char *name
)
113 if (! FetchUninstallInformation())
116 for (i
=0; i
< numentries
; i
++)
118 if (strcmp(entries
[i
].key
, name
) == 0)
129 fprintf(stderr
, "Error: could not match application [%s]\n", name
);
133 int main( int argc
, char *argv
[])
140 HINSTANCE hInst
= NULL
;
146 /* Handle requests just to list the applications */
147 if( !lstrcmpA( token
, "--list" ) )
149 ListUninstallPrograms();
152 else if( !lstrcmpA( token
, "--remove" ) )
156 WINE_ERR( "The remove option requires a parameter.\n");
160 RemoveSpecificProgram( argv
[i
++] );
165 WINE_ERR( "unknown option %s\n",token
);
170 LoadString( hInst
, IDS_APPNAME
, appname
, sizeof(appname
));
173 wc
.lpfnWndProc
= MainProc
;
176 wc
.hInstance
= hInst
;
177 wc
.hIcon
= LoadIcon( hInst
, appname
);
178 wc
.hCursor
= LoadCursor( NULL_HANDLE
, IDI_APPLICATION
);
179 wc
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
180 wc
.lpszMenuName
= NULL
;
181 wc
.lpszClassName
= appname
;
183 if (!RegisterClass(&wc
)) exit(1);
184 hWnd
= CreateWindow( appname
, "Wine Application Uninstaller",
185 WS_OVERLAPPEDWINDOW
& ~WS_THICKFRAME
& ~WS_MAXIMIZEBOX
,
186 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
187 NULL_HANDLE
, NULL_HANDLE
, hInst
, NULL
);
191 ShowWindow( hWnd
, SW_SHOW
);
192 UpdateWindow( hWnd
);
194 while( GetMessage(&msg
, NULL_HANDLE
, 0, 0) ) {
195 TranslateMessage( &msg
);
196 DispatchMessage( &msg
);
201 int cmp_by_name(const void *a
, const void *b
)
203 return lstrcmpiW(((const uninst_entry
*)a
)->descr
, ((const uninst_entry
*)b
)->descr
);
206 int FetchUninstallInformation(void)
208 HKEY hkeyUninst
, hkeyApp
;
210 DWORD sizeOfSubKeyName
, displen
, uninstlen
;
211 char subKeyName
[256];
217 if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE
, REGSTR_PATH_UNINSTALL
,
218 0, KEY_READ
, &hkeyUninst
) != ERROR_SUCCESS
)
220 MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname
, MB_OK
);
225 entries
= HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry
));
227 strcpy(key_app
, REGSTR_PATH_UNINSTALL
);
228 strcat(key_app
, "\\");
229 p
= key_app
+strlen(REGSTR_PATH_UNINSTALL
)+1;
231 sizeOfSubKeyName
= 255;
233 RegEnumKeyExA( hkeyUninst
, i
, subKeyName
, &sizeOfSubKeyName
,
234 NULL
, NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
;
237 static const WCHAR DisplayNameW
[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
239 strcpy(p
, subKeyName
);
240 RegOpenKeyExA(HKEY_LOCAL_MACHINE
, key_app
, 0, KEY_READ
, &hkeyApp
);
242 if ( (RegQueryValueExW(hkeyApp
, DisplayNameW
,
243 0, 0, NULL
, &displen
) == ERROR_SUCCESS
)
244 && (RegQueryValueExA(hkeyApp
, REGSTR_VAL_UNINSTALLER_COMMANDLINE
,
245 0, 0, NULL
, &uninstlen
) == ERROR_SUCCESS
) )
248 entries
= HeapReAlloc(GetProcessHeap(), 0, entries
, numentries
*sizeof(uninst_entry
));
249 entries
[numentries
-1].key
=
250 HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName
)+1);
251 strcpy(entries
[numentries
-1].key
, subKeyName
);
252 entries
[numentries
-1].descr
=
253 HeapAlloc(GetProcessHeap(), 0, displen
);
254 RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0,
255 (LPBYTE
)entries
[numentries
-1].descr
, &displen
);
256 entries
[numentries
-1].command
=
257 HeapAlloc(GetProcessHeap(), 0, uninstlen
);
258 entries
[numentries
-1].active
= 0;
259 RegQueryValueExA(hkeyApp
, REGSTR_VAL_UNINSTALLER_COMMANDLINE
, 0, 0,
260 entries
[numentries
-1].command
, &uninstlen
);
261 WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
262 numentries
, entries
[numentries
-1].key
,
263 wine_dbgstr_w(entries
[numentries
-1].descr
),
264 entries
[numentries
-1].command
);
266 RegCloseKey(hkeyApp
);
268 sizeOfSubKeyName
= 255;
270 qsort(entries
, numentries
, sizeof(uninst_entry
), cmp_by_name
);
271 RegCloseKey(hkeyUninst
);
275 void UninstallProgram(void)
281 PROCESS_INFORMATION info
;
287 for (i
=0; i
< numentries
; i
++)
289 if (!(entries
[i
].active
)) /* don't uninstall this one */
291 WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries
[i
].descr
));
292 memset(&si
, 0, sizeof(STARTUPINFO
));
293 si
.cb
= sizeof(STARTUPINFO
);
294 si
.wShowWindow
= SW_NORMAL
;
295 res
= CreateProcess(NULL
, entries
[i
].command
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &info
);
297 { /* wait for the process to exit */
298 WaitForSingleObject(info
.hProcess
, INFINITE
);
299 res
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
300 WINE_TRACE("%d: %08lx\n", res
, exit_code
);
302 /* delete the application's uninstall entry */
303 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REGSTR_PATH_UNINSTALL
,
304 0, KEY_READ
, &hkey
) == ERROR_SUCCESS
)
306 RegDeleteKey(hkey
, entries
[i
].key
);
313 sprintf(errormsg
, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries
[i
].command
);
314 MessageBox(0, errormsg
, appname
, MB_OK
);
317 WINE_TRACE("finished uninstall phase.\n");
318 list_need_update
= 1;
321 LRESULT WINAPI
MainProc( HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
323 HFONT static_font
, listbox_font
;
328 int cxChar
, cyChar
, y
, bx
, maxx
, maxy
, wx
, wy
;
329 static HWND hwndList
= 0, static_text
= 0;
337 GetTextMetrics(hdc
, &tm
);
338 static_font
= CreateFont(tm
.tmHeight
+ tm
.tmExternalLeading
, 0, 0, 0, 600, FALSE
, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
339 listbox_font
= CreateFont(tm
.tmHeight
+ tm
.tmExternalLeading
, 0, 0, 0, 0, TRUE
, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
340 cxChar
= tm
.tmAveCharWidth
;
341 cyChar
= tm
.tmHeight
+ tm
.tmExternalLeading
;
342 ReleaseDC(hWnd
, hdc
);
343 /* FIXME: implement sorting and use LBS_SORT here ! */
344 style
= (WS_CHILD
|WS_VISIBLE
|LBS_STANDARD
) & ~LBS_SORT
;
345 #ifdef USE_MULTIPLESEL
346 style
|= LBS_MULTIPLESEL
;
348 bx
= maxx
= cxChar
* 3;
349 y
= maxy
= cyChar
* 1;
350 static_text
= CreateWindow("static", program_description
,
351 WS_CHILD
|WS_VISIBLE
|SS_LEFT
,
353 cxChar
* sizeof(program_description
), cyChar
* 1,
355 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
356 SendMessage(static_text
, WM_SETFONT
, (WPARAM
)static_font
, MAKELPARAM(FALSE
, 0));
357 maxy
+= cyChar
* 2; /*static text + distance */
358 hwndList
= CreateWindow("listbox", NULL
,
361 cxChar
* 50 + GetSystemMetrics(SM_CXVSCROLL
), cyChar
* 10,
363 (HINSTANCE
)GetWindowLong(hWnd
, GWL_HINSTANCE
), NULL
);
364 SendMessage(hwndList
, WM_SETFONT
, (WPARAM
)listbox_font
, MAKELPARAM(FALSE
, 0));
365 GetWindowRect(hwndList
, &rect
);
366 maxx
+= (rect
.right
- rect
.left
)*1.1;
367 maxy
+= (rect
.bottom
- rect
.top
)*1.1;
371 for (i
=0; i
< NUM
; i
++)
373 button
[i
].hwnd
= CreateWindow("button", button
[i
].text
,
374 WS_CHILD
|WS_VISIBLE
|BS_PUSHBUTTON
,
378 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
383 maxx
+= wx
+ cxChar
* 4;
384 maxy
+= cyChar
* 2; /* window border */
385 SetWindowPos( hWnd
, 0,
393 hdc
= BeginPaint( hWnd
, &ps
);
394 if (list_need_update
)
397 prevsel
= SendMessage(hwndList
, LB_GETCURSEL
, 0, 0);
398 if (!(FetchUninstallInformation()))
403 SendMessage(hwndList
, LB_RESETCONTENT
, 0, 0);
404 SendMessage(hwndList
, WM_SETREDRAW
, FALSE
, 0);
405 for (i
=0; i
< numentries
; i
++)
407 WINE_TRACE("adding %s\n", wine_dbgstr_w(entries
[i
].descr
));
408 SendMessageW(hwndList
, LB_ADDSTRING
, 0, (LPARAM
)entries
[i
].descr
);
410 WINE_TRACE("setting prevsel %d\n", prevsel
);
412 SendMessage(hwndList
, LB_SETCURSEL
, prevsel
, 0 );
413 SendMessage(hwndList
, WM_SETREDRAW
, TRUE
, 0);
414 list_need_update
= 0;
416 EndPaint( hWnd
, &ps
);
421 PostQuitMessage( 0 );
425 if ((HWND
)lParam
== hwndList
)
427 if (HIWORD(wParam
) == LBN_SELCHANGE
)
429 int sel
= SendMessage(hwndList
, LB_GETCURSEL
, 0, 0);
431 #ifndef USE_MULTIPLESEL
434 entries
[oldsel
].active
^= 1; /* toggle */
435 WINE_TRACE("toggling %d old %s\n", entries
[oldsel
].active
,
436 wine_dbgstr_w(entries
[oldsel
].descr
));
439 entries
[sel
].active
^= 1; /* toggle */
440 WINE_TRACE("toggling %d %s\n", entries
[sel
].active
,
441 wine_dbgstr_w(entries
[oldsel
].descr
));
446 if ((HWND
)lParam
== button
[0].hwnd
) /* Uninstall button */
450 InvalidateRect(hWnd
, NULL
, TRUE
);
455 if ((HWND
)lParam
== button
[1].hwnd
) /* About button */
456 MessageBox(0, about_string
, "About Wine Application Uninstaller", MB_OK
);
458 if ((HWND
)lParam
== button
[2].hwnd
) /* Exit button */
463 return( DefWindowProc( hWnd
, msg
, wParam
, lParam
));