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 "Wine Application Uninstaller (C) 2004 by Andreas Mohr <andi@lisas.de> and Hannu Valtonen <Hannu.Valtonen@hut.fi>";
56 static char program_description
[] =
57 "Please select the application you wish to uninstall:";
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)
93 if (! FetchUninstallInformation())
96 for (i
=0; i
< numentries
; i
++)
98 len
= WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].descr
, -1, NULL
, 0, NULL
, NULL
);
99 descr
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
100 WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].descr
, -1, descr
, len
, NULL
, NULL
);
101 printf("%s|||%s\n", entries
[i
].key
, descr
);
102 HeapFree(GetProcessHeap(), 0, descr
);
107 void RemoveSpecificProgram(char *name
)
111 if (! FetchUninstallInformation())
114 for (i
=0; i
< numentries
; i
++)
116 if (strcmp(entries
[i
].key
, name
) == 0)
127 fprintf(stderr
, "Error: could not match application [%s]\n", name
);
131 int main( int argc
, char *argv
[])
138 HINSTANCE hInst
= NULL
;
144 /* Handle requests just to list the applications */
145 if( !lstrcmpA( token
, "--list" ) )
147 ListUninstallPrograms();
150 else if( !lstrcmpA( token
, "--remove" ) )
154 WINE_ERR( "The remove option requires a parameter.\n");
158 RemoveSpecificProgram( argv
[i
++] );
163 WINE_ERR( "unknown option %s\n",token
);
168 LoadString( hInst
, IDS_APPNAME
, appname
, sizeof(appname
));
171 wc
.lpfnWndProc
= MainProc
;
174 wc
.hInstance
= hInst
;
175 wc
.hIcon
= LoadIcon( hInst
, appname
);
176 wc
.hCursor
= LoadCursor( NULL_HANDLE
, IDI_APPLICATION
);
177 wc
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
178 wc
.lpszMenuName
= NULL
;
179 wc
.lpszClassName
= appname
;
181 if (!RegisterClass(&wc
)) exit(1);
182 hWnd
= CreateWindow( appname
, "Wine Application Uninstaller",
183 WS_OVERLAPPEDWINDOW
& ~WS_THICKFRAME
& ~WS_MAXIMIZEBOX
,
184 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
185 NULL_HANDLE
, NULL_HANDLE
, hInst
, NULL
);
189 ShowWindow( hWnd
, SW_SHOW
);
190 UpdateWindow( hWnd
);
192 while( GetMessage(&msg
, NULL_HANDLE
, 0, 0) ) {
193 TranslateMessage( &msg
);
194 DispatchMessage( &msg
);
199 int cmp_by_name(const void *a
, const void *b
)
201 return lstrcmpiW(((uninst_entry
*)a
)->descr
, ((uninst_entry
*)b
)->descr
);
204 int FetchUninstallInformation(void)
206 HKEY hkeyUninst
, hkeyApp
;
208 DWORD sizeOfSubKeyName
, displen
, uninstlen
;
209 char subKeyName
[256];
215 if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE
, REGSTR_PATH_UNINSTALL
,
216 0, KEY_READ
, &hkeyUninst
) != ERROR_SUCCESS
)
218 MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname
, MB_OK
);
223 entries
= HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry
));
225 strcpy(key_app
, REGSTR_PATH_UNINSTALL
);
226 strcat(key_app
, "\\");
227 p
= key_app
+strlen(REGSTR_PATH_UNINSTALL
)+1;
229 sizeOfSubKeyName
= 255;
231 RegEnumKeyExA( hkeyUninst
, i
, subKeyName
, &sizeOfSubKeyName
,
232 NULL
, NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
;
235 static const WCHAR DisplayNameW
[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
237 strcpy(p
, subKeyName
);
238 RegOpenKeyExA(HKEY_LOCAL_MACHINE
, key_app
, 0, KEY_READ
, &hkeyApp
);
240 if ( (RegQueryValueExW(hkeyApp
, DisplayNameW
,
241 0, 0, NULL
, &displen
) == ERROR_SUCCESS
)
242 && (RegQueryValueExA(hkeyApp
, REGSTR_VAL_UNINSTALLER_COMMANDLINE
,
243 0, 0, NULL
, &uninstlen
) == ERROR_SUCCESS
) )
246 entries
= HeapReAlloc(GetProcessHeap(), 0, entries
, numentries
*sizeof(uninst_entry
));
247 entries
[numentries
-1].key
=
248 HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName
)+1);
249 strcpy(entries
[numentries
-1].key
, subKeyName
);
250 entries
[numentries
-1].descr
=
251 HeapAlloc(GetProcessHeap(), 0, displen
);
252 RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0,
253 (LPBYTE
)entries
[numentries
-1].descr
, &displen
);
254 entries
[numentries
-1].command
=
255 HeapAlloc(GetProcessHeap(), 0, uninstlen
);
256 entries
[numentries
-1].active
= 0;
257 RegQueryValueExA(hkeyApp
, REGSTR_VAL_UNINSTALLER_COMMANDLINE
, 0, 0,
258 entries
[numentries
-1].command
, &uninstlen
);
259 WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
260 numentries
, entries
[numentries
-1].key
,
261 wine_dbgstr_w(entries
[numentries
-1].descr
),
262 entries
[numentries
-1].command
);
264 RegCloseKey(hkeyApp
);
266 sizeOfSubKeyName
= 255;
268 qsort(entries
, numentries
, sizeof(uninst_entry
), cmp_by_name
);
269 RegCloseKey(hkeyUninst
);
273 void UninstallProgram(void)
279 PROCESS_INFORMATION info
;
285 for (i
=0; i
< numentries
; i
++)
287 if (!(entries
[i
].active
)) /* don't uninstall this one */
289 WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries
[i
].descr
));
290 memset(&si
, 0, sizeof(STARTUPINFO
));
291 si
.cb
= sizeof(STARTUPINFO
);
292 si
.wShowWindow
= SW_NORMAL
;
293 res
= CreateProcess(NULL
, entries
[i
].command
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &info
);
295 { /* wait for the process to exit */
296 WaitForSingleObject(info
.hProcess
, INFINITE
);
297 res
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
298 WINE_TRACE("%d: %08lx\n", res
, exit_code
);
300 /* delete the application's uninstall entry */
301 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REGSTR_PATH_UNINSTALL
,
302 0, KEY_READ
, &hkey
) == ERROR_SUCCESS
)
304 RegDeleteKey(hkey
, entries
[i
].key
);
311 sprintf(errormsg
, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries
[i
].command
);
312 MessageBox(0, errormsg
, appname
, MB_OK
);
315 WINE_TRACE("finished uninstall phase.\n");
316 list_need_update
= 1;
319 LRESULT WINAPI
MainProc( HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
324 int cxChar
, cyChar
, i
, y
, bx
, maxx
, maxy
, wx
, wy
;
325 static HWND hwndList
= 0, hwndEdit
= 0;
333 GetTextMetrics(hdc
, &tm
);
334 cxChar
= tm
.tmAveCharWidth
;
335 cyChar
= tm
.tmHeight
+ tm
.tmExternalLeading
;
336 ReleaseDC(hWnd
, hdc
);
337 /* FIXME: implement sorting and use LBS_SORT here ! */
338 style
= (WS_CHILD
|WS_VISIBLE
|LBS_STANDARD
) & ~LBS_SORT
;
339 #ifdef USE_MULTIPLESEL
340 style
|= LBS_MULTIPLESEL
;
342 bx
= maxx
= cxChar
* 5;
343 y
= maxy
= cyChar
* 1;
344 CreateWindow("static", program_description
,
345 WS_CHILD
|WS_VISIBLE
|SS_LEFT
,
347 cxChar
* sizeof(program_description
), cyChar
* 1,
349 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
350 maxy
+= cyChar
* 2; /*static text + distance */
351 hwndList
= CreateWindow("listbox", NULL
,
354 cxChar
* 50 + GetSystemMetrics(SM_CXVSCROLL
), cyChar
* 10,
356 (HINSTANCE
)GetWindowLong(hWnd
, GWL_HINSTANCE
), NULL
);
357 GetWindowRect(hwndList
, &rect
);
358 maxx
+= (rect
.right
- rect
.left
)*1.1;
359 maxy
+= (rect
.bottom
- rect
.top
)*1.1;
363 for (i
=0; i
< NUM
; i
++)
365 button
[i
].hwnd
= CreateWindow("button", button
[i
].text
,
366 WS_CHILD
|WS_VISIBLE
|BS_PUSHBUTTON
,
370 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
375 maxx
+= wx
+ cxChar
* 5;
376 CreateWindow("static", "Command line to be executed:",
377 WS_CHILD
|WS_VISIBLE
|SS_LEFT
,
381 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
383 hwndEdit
= CreateWindow("edit", NULL
,
384 WS_CHILD
|WS_VISIBLE
|WS_BORDER
|ES_LEFT
|ES_MULTILINE
|ES_READONLY
,
385 bx
, maxy
, maxx
-(2*bx
), (cyChar
*2)+4,
387 ((LPCREATESTRUCT
)lParam
)->hInstance
, NULL
);
388 maxy
+= (cyChar
*2)+4 + cyChar
* 3; /* edit ctrl + bottom border */
389 SetWindowPos( hWnd
, 0,
397 hdc
= BeginPaint( hWnd
, &ps
);
398 if (list_need_update
)
401 prevsel
= SendMessage(hwndList
, LB_GETCURSEL
, 0, 0);
402 if (!(FetchUninstallInformation()))
407 SendMessage(hwndList
, LB_RESETCONTENT
, 0, 0);
408 SendMessage(hwndList
, WM_SETREDRAW
, FALSE
, 0);
409 for (i
=0; i
< numentries
; i
++)
411 WINE_TRACE("adding %s\n", wine_dbgstr_w(entries
[i
].descr
));
412 SendMessageW(hwndList
, LB_ADDSTRING
, 0, (LPARAM
)entries
[i
].descr
);
414 WINE_TRACE("setting prevsel %d\n", prevsel
);
416 SendMessage(hwndList
, LB_SETCURSEL
, prevsel
, 0 );
417 SendMessage(hwndList
, WM_SETREDRAW
, TRUE
, 0);
418 list_need_update
= 0;
420 EndPaint( hWnd
, &ps
);
425 PostQuitMessage( 0 );
429 if ((HWND
)lParam
== hwndList
)
431 if (HIWORD(wParam
) == LBN_SELCHANGE
)
433 int sel
= SendMessage(hwndList
, LB_GETCURSEL
, 0, 0);
435 #ifndef USE_MULTIPLESEL
438 entries
[oldsel
].active
^= 1; /* toggle */
439 WINE_TRACE("toggling %d old %s\n", entries
[oldsel
].active
,
440 wine_dbgstr_w(entries
[oldsel
].descr
));
443 entries
[sel
].active
^= 1; /* toggle */
444 WINE_TRACE("toggling %d %s\n", entries
[sel
].active
,
445 wine_dbgstr_w(entries
[oldsel
].descr
));
446 SendMessage(hwndEdit
, WM_SETTEXT
, 0, (LPARAM
)entries
[sel
].command
);
451 if ((HWND
)lParam
== button
[0].hwnd
) /* Uninstall button */
455 InvalidateRect(hWnd
, NULL
, TRUE
);
460 if ((HWND
)lParam
== button
[1].hwnd
) /* About button */
461 MessageBox(0, about_string
, "About Wine Application Uninstaller", MB_OK
);
463 if ((HWND
)lParam
== button
[2].hwnd
) /* Exit button */
468 return( DefWindowProc( hWnd
, msg
, wParam
, lParam
));