4 * Copyright 2000 Andreas Mohr
5 * Copyright 2004 Hannu Valtonen
6 * Copyright 2005 Jonathan Ernst
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(uninstaller
);
41 static uninst_entry
*entries
= NULL
;
42 static unsigned int numentries
= 0;
43 static int list_need_update
= 1;
44 static int oldsel
= -1;
45 static WCHAR
*sFilter
;
46 static WCHAR sAppName
[MAX_STRING_LEN
];
47 static WCHAR sAboutTitle
[MAX_STRING_LEN
];
48 static WCHAR sAbout
[MAX_STRING_LEN
];
49 static WCHAR sRegistryKeyNotAvailable
[MAX_STRING_LEN
];
50 static WCHAR sUninstallFailed
[MAX_STRING_LEN
];
52 static int FetchUninstallInformation(void);
53 static void UninstallProgram(void);
54 static void UpdateList(HWND hList
);
55 static int cmp_by_name(const void *a
, const void *b
);
56 static INT_PTR CALLBACK
DlgProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
);
59 static const WCHAR BackSlashW
[] = { '\\', 0 };
60 static const WCHAR DisplayNameW
[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
61 static const WCHAR PathUninstallW
[] = {
62 'S','o','f','t','w','a','r','e','\\',
63 'M','i','c','r','o','s','o','f','t','\\',
64 'W','i','n','d','o','w','s','\\',
65 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
66 'U','n','i','n','s','t','a','l','l',0 };
67 static const WCHAR UninstallCommandlineW
[] = {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
71 * Used to output program list when used with --list
73 static void ListUninstallPrograms(void)
80 FetchUninstallInformation();
82 for (i
=0; i
< numentries
; i
++)
84 lenDescr
= WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].descr
, -1, NULL
, 0, NULL
, NULL
);
85 lenKey
= WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].key
, -1, NULL
, 0, NULL
, NULL
);
86 descr
= HeapAlloc(GetProcessHeap(), 0, lenDescr
);
87 key
= HeapAlloc(GetProcessHeap(), 0, lenKey
);
88 WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].descr
, -1, descr
, lenDescr
, NULL
, NULL
);
89 WideCharToMultiByte(CP_UNIXCP
, 0, entries
[i
].key
, -1, key
, lenKey
, NULL
, NULL
);
90 printf("%s|||%s\n", key
, descr
);
91 HeapFree(GetProcessHeap(), 0, descr
);
92 HeapFree(GetProcessHeap(), 0, key
);
97 static void RemoveSpecificProgram(WCHAR
*nameW
)
103 FetchUninstallInformation();
105 for (i
=0; i
< numentries
; i
++)
107 if (lstrcmpW(entries
[i
].key
, nameW
) == 0)
118 lenName
= WideCharToMultiByte(CP_UNIXCP
, 0, nameW
, -1, NULL
, 0, NULL
, NULL
);
119 name
= HeapAlloc(GetProcessHeap(), 0, lenName
);
120 WideCharToMultiByte(CP_UNIXCP
, 0, nameW
, -1, name
, lenName
, NULL
, NULL
);
121 fprintf(stderr
, "Error: could not match application [%s]\n", name
);
122 HeapFree(GetProcessHeap(), 0, name
);
127 int wmain(int argc
, WCHAR
*argv
[])
129 LPCWSTR token
= NULL
;
130 HINSTANCE hInst
= GetModuleHandleW(0);
131 static const WCHAR listW
[] = { '-','-','l','i','s','t',0 };
132 static const WCHAR removeW
[] = { '-','-','r','e','m','o','v','e',0 };
139 /* Handle requests just to list the applications */
140 if( !lstrcmpW( token
, listW
) )
142 ListUninstallPrograms();
145 else if( !lstrcmpW( token
, removeW
) )
149 WINE_ERR( "The remove option requires a parameter.\n");
153 RemoveSpecificProgram( argv
[i
++] );
158 WINE_ERR( "unknown option %s\n",wine_dbgstr_w(token
));
163 /* Load MessageBox's strings */
164 LoadStringW(hInst
, IDS_APPNAME
, sAppName
, sizeof(sAppName
)/sizeof(WCHAR
));
165 LoadStringW(hInst
, IDS_ABOUTTITLE
, sAboutTitle
, sizeof(sAboutTitle
)/sizeof(WCHAR
));
166 LoadStringW(hInst
, IDS_ABOUT
, sAbout
, sizeof(sAbout
)/sizeof(WCHAR
));
167 LoadStringW(hInst
, IDS_REGISTRYKEYNOTAVAILABLE
, sRegistryKeyNotAvailable
, sizeof(sRegistryKeyNotAvailable
)/sizeof(WCHAR
));
168 LoadStringW(hInst
, IDS_UNINSTALLFAILED
, sUninstallFailed
, sizeof(sUninstallFailed
)/sizeof(WCHAR
));
170 return DialogBoxW(hInst
, MAKEINTRESOURCEW(IDD_UNINSTALLER
), NULL
, DlgProc
);
175 * Used to sort entries by name.
177 static int cmp_by_name(const void *a
, const void *b
)
179 return lstrcmpiW(((const uninst_entry
*)a
)->descr
, ((const uninst_entry
*)b
)->descr
);
184 * Fetch information from the uninstall key.
186 static int FetchFromRootKey(HKEY root
)
188 HKEY hkeyUninst
, hkeyApp
;
190 DWORD sizeOfSubKeyName
, displen
, uninstlen
;
191 WCHAR subKeyName
[256];
195 if (RegOpenKeyExW(root
, PathUninstallW
, 0, KEY_READ
, &hkeyUninst
) != ERROR_SUCCESS
)
198 lstrcpyW(key_app
, PathUninstallW
);
199 lstrcatW(key_app
, BackSlashW
);
200 p
= key_app
+lstrlenW(PathUninstallW
)+1;
202 sizeOfSubKeyName
= 255;
203 for (i
=0; RegEnumKeyExW( hkeyUninst
, i
, subKeyName
, &sizeOfSubKeyName
, NULL
, NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
; ++i
)
205 lstrcpyW(p
, subKeyName
);
206 RegOpenKeyExW(root
, key_app
, 0, KEY_READ
, &hkeyApp
);
207 if ((RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0, NULL
, &displen
) == ERROR_SUCCESS
)
208 && (RegQueryValueExW(hkeyApp
, UninstallCommandlineW
, 0, 0, NULL
, &uninstlen
) == ERROR_SUCCESS
))
211 entries
= HeapReAlloc(GetProcessHeap(), 0, entries
, numentries
*sizeof(uninst_entry
));
212 entries
[numentries
-1].root
= root
;
213 entries
[numentries
-1].key
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(subKeyName
)+1)*sizeof(WCHAR
));
214 lstrcpyW(entries
[numentries
-1].key
, subKeyName
);
215 entries
[numentries
-1].descr
= HeapAlloc(GetProcessHeap(), 0, displen
);
216 RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0, (LPBYTE
)entries
[numentries
-1].descr
, &displen
);
217 entries
[numentries
-1].command
= HeapAlloc(GetProcessHeap(), 0, uninstlen
);
218 entries
[numentries
-1].active
= 0;
219 RegQueryValueExW(hkeyApp
, UninstallCommandlineW
, 0, 0, (LPBYTE
)entries
[numentries
-1].command
, &uninstlen
);
220 WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
221 numentries
, wine_dbgstr_w(entries
[numentries
-1].key
), wine_dbgstr_w(entries
[numentries
-1].descr
), wine_dbgstr_w(entries
[numentries
-1].command
));
222 if(sFilter
!= NULL
&& StrStrIW(entries
[numentries
-1].descr
,sFilter
)==NULL
)
225 RegCloseKey(hkeyApp
);
226 sizeOfSubKeyName
= 255;
228 RegCloseKey(hkeyUninst
);
233 static int FetchUninstallInformation(void)
240 entries
= HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry
));
242 rc
= FetchFromRootKey(HKEY_LOCAL_MACHINE
);
243 rc
|= FetchFromRootKey(HKEY_CURRENT_USER
);
245 qsort(entries
, numentries
, sizeof(uninst_entry
), cmp_by_name
);
249 static void UninstallProgram(void)
252 WCHAR errormsg
[1024];
255 PROCESS_INFORMATION info
;
258 for (i
=0; i
< numentries
; i
++)
260 if (!(entries
[i
].active
)) /* don't uninstall this one */
262 WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries
[i
].descr
));
263 memset(&si
, 0, sizeof(STARTUPINFOW
));
264 si
.cb
= sizeof(STARTUPINFOW
);
265 si
.wShowWindow
= SW_NORMAL
;
266 res
= CreateProcessW(NULL
, entries
[i
].command
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &info
);
268 { /* wait for the process to exit */
269 WaitForSingleObject(info
.hProcess
, INFINITE
);
270 res
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
271 WINE_TRACE("%d: %08x\n", res
, exit_code
);
275 wsprintfW(errormsg
, sUninstallFailed
, entries
[i
].command
);
276 if(MessageBoxW(0, errormsg
, sAppName
, MB_YESNO
| MB_ICONQUESTION
)==IDYES
)
278 /* delete the application's uninstall entry */
279 RegOpenKeyExW(entries
[i
].root
, PathUninstallW
, 0, KEY_READ
, &hkey
);
280 RegDeleteKeyW(hkey
, entries
[i
].key
);
285 WINE_TRACE("finished uninstall phase.\n");
286 list_need_update
= 1;
289 static void UpdateButtons(HWND hDlg
, HWND hList
)
291 EnableWindow(GetDlgItem(hDlg
, IDC_UNINSTALL
), SendMessageW(hList
, LB_GETSELCOUNT
, 0, 0) > 0);
294 static INT_PTR CALLBACK
DlgProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
298 HWND hList
= GetDlgItem(hwnd
, IDC_LIST
);
303 GetTextMetricsW(hdc
, &tm
);
305 ReleaseDC(hwnd
, hdc
);
306 UpdateButtons(hwnd
, hList
);
309 switch(LOWORD(wParam
))
313 if (HIWORD(wParam
) == EN_CHANGE
)
315 int len
= GetWindowTextLengthW(GetDlgItem(hwnd
, IDC_FILTER
));
316 list_need_update
= 1;
319 sFilter
= (WCHAR
*)GlobalAlloc(GPTR
, (len
+ 1)*sizeof(WCHAR
));
320 GetDlgItemTextW(hwnd
, IDC_FILTER
, sFilter
, len
+ 1);
324 UpdateButtons(hwnd
, hList
);
330 int count
= SendMessageW(hList
, LB_GETSELCOUNT
, 0, 0);
335 UpdateButtons(hwnd
, hList
);
340 if (HIWORD(wParam
) == LBN_SELCHANGE
)
342 int sel
= SendMessageW(hList
, LB_GETCURSEL
, 0, 0);
345 entries
[oldsel
].active
^= 1; /* toggle */
346 WINE_TRACE("toggling %d old %s\n", entries
[oldsel
].active
,
347 wine_dbgstr_w(entries
[oldsel
].descr
));
349 entries
[sel
].active
^= 1; /* toggle */
350 WINE_TRACE("toggling %d %s\n", entries
[sel
].active
,
351 wine_dbgstr_w(entries
[sel
].descr
));
354 UpdateButtons(hwnd
, hList
);
357 MessageBoxW(0, sAbout
, sAboutTitle
, MB_OK
);
372 static void UpdateList(HWND hList
)
375 if (list_need_update
)
378 prevsel
= SendMessageW(hList
, LB_GETCURSEL
, 0, 0);
379 if (!(FetchUninstallInformation()))
381 MessageBoxW(0, sRegistryKeyNotAvailable
, sAppName
, MB_OK
);
385 SendMessageW(hList
, LB_RESETCONTENT
, 0, 0);
386 SendMessageW(hList
, WM_SETREDRAW
, FALSE
, 0);
387 for (i
=0; i
< numentries
; i
++)
389 WINE_TRACE("adding %s\n", wine_dbgstr_w(entries
[i
].descr
));
390 SendMessageW(hList
, LB_ADDSTRING
, 0, (LPARAM
)entries
[i
].descr
);
392 WINE_TRACE("setting prevsel %d\n", prevsel
);
394 SendMessageW(hList
, LB_SETCURSEL
, prevsel
, 0 );
395 SendMessageW(hList
, WM_SETREDRAW
, TRUE
, 0);
396 list_need_update
= 0;