2 * Add/Remove Programs applet
3 * Partially based on Wine Uninstaller
5 * Copyright 2000 Andreas Mohr
6 * Copyright 2004 Hannu Valtonen
7 * Copyright 2005 Jonathan Ernst
8 * Copyright 2001-2002, 2008 Owen Rudge
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/port.h"
28 #include "wine/unicode.h"
29 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl);
48 /* define a maximum length for various buffers we use */
49 #define MAX_STRING_LEN 1024
51 typedef struct APPINFO {
64 WCHAR regkey[MAX_STRING_LEN];
69 static struct APPINFO *AppInfo = NULL;
70 static HINSTANCE hInst;
72 /* names of registry keys */
73 static const WCHAR BackSlashW[] = { '\\', 0 };
74 static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
75 static const WCHAR DisplayIconW[] = {'D','i','s','p','l','a','y','I','c','o','n',0};
76 static const WCHAR DisplayVersionW[] = {'D','i','s','p','l','a','y','V','e','r',
78 static const WCHAR PublisherW[] = {'P','u','b','l','i','s','h','e','r',0};
79 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
80 'S','t','r','i','n','g',0};
82 static const WCHAR PathUninstallW[] = {
83 'S','o','f','t','w','a','r','e','\\',
84 'M','i','c','r','o','s','o','f','t','\\',
85 'W','i','n','d','o','w','s','\\',
86 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
87 'U','n','i','n','s','t','a','l','l',0 };
89 /******************************************************************************
91 * Description: Entry point for DLL file
93 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
96 TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
100 case DLL_PROCESS_ATTACH:
107 /******************************************************************************
109 * Description: Frees memory used by an AppInfo structure, and any children.
111 static void FreeAppInfo(APPINFO *info)
113 APPINFO *iter = info;
118 HeapFree(GetProcessHeap(), 0, iter->title);
121 HeapFree(GetProcessHeap(), 0, iter->path);
124 HeapFree(GetProcessHeap(), 0, iter->icon);
127 HeapFree(GetProcessHeap(), 0, iter->publisher);
130 HeapFree(GetProcessHeap(), 0, iter->version);
133 HeapFree(GetProcessHeap(), 0, iter);
137 /******************************************************************************
138 * Name : ReadApplicationsFromRegistry
139 * Description: Creates a linked list of uninstallable applications from the
141 * Parameters : root - Which registry root to read from (HKCU/HKLM)
142 * Returns : TRUE if successful, FALSE otherwise
144 static BOOL ReadApplicationsFromRegistry(HKEY root)
146 HKEY hkeyUninst, hkeyApp;
148 DWORD sizeOfSubKeyName, displen, uninstlen;
149 WCHAR subKeyName[256];
150 WCHAR key_app[MAX_STRING_LEN];
152 APPINFO *iter = AppInfo;
156 if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
160 lstrcpyW(key_app, PathUninstallW);
161 lstrcatW(key_app, BackSlashW);
162 p = key_app+lstrlenW(PathUninstallW)+1;
164 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
168 /* find the end of the list */
169 for (iter = AppInfo; iter->next; iter = iter->next);
172 for (i = 0; RegEnumKeyExW(hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL,
173 NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; ++i)
175 lstrcpyW(p, subKeyName);
176 RegOpenKeyExW(root, key_app, 0, KEY_READ, &hkeyApp);
181 if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) ==
182 ERROR_SUCCESS) && (RegQueryValueExW(hkeyApp, UninstallCommandlineW,
183 0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
185 /* if we already have iter, allocate the next entry */
188 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
189 sizeof(struct APPINFO));
198 /* if not, start the list */
199 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
200 sizeof(struct APPINFO));
206 iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
211 RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
214 /* now get DisplayIcon */
216 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
222 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
227 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
230 /* separate the index from the icon name, if supplied */
231 iconPtr = strchrW(iter->icon, ',');
236 iter->iconIdx = atoiW(iconPtr);
240 iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
245 RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
246 (LPBYTE)iter->path, &uninstlen);
248 /* publisher, version */
249 if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
252 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
254 if (!iter->publisher)
257 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
261 if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
264 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
269 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
274 iter->regroot = root;
275 lstrcpyW(iter->regkey, subKeyName);
280 RegCloseKey(hkeyApp);
281 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
288 RegCloseKey(hkeyApp);
292 RegCloseKey(hkeyUninst);
297 /******************************************************************************
298 * Name : AddApplicationsToList
299 * Description: Populates the list box with applications.
300 * Parameters : hWnd - Handle of the dialog box
302 static void AddApplicationsToList(HWND hWnd, HIMAGELIST hList)
304 APPINFO *iter = AppInfo;
316 if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
318 index = ImageList_AddIcon(hList, hIcon);
323 lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
324 lvItem.iItem = iter->id;
326 lvItem.pszText = iter->title;
327 lvItem.iImage = index;
328 lvItem.lParam = iter->id;
330 index = ListView_InsertItemW(hWnd, &lvItem);
332 /* now add the subitems (columns) */
333 ListView_SetItemTextW(hWnd, index, 1, iter->publisher);
334 ListView_SetItemTextW(hWnd, index, 2, iter->version);
340 /******************************************************************************
341 * Name : RemoveItemsFromList
342 * Description: Clears the application list box.
343 * Parameters : hWnd - Handle of the dialog box
345 static void RemoveItemsFromList(HWND hWnd)
347 SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_DELETEALLITEMS, 0, 0);
350 /******************************************************************************
352 * Description: Frees memory used by the application linked list.
354 static inline void EmptyList(void)
356 FreeAppInfo(AppInfo);
360 /******************************************************************************
361 * Name : UpdateButtons
362 * Description: Enables/disables the Add/Remove button depending on current
363 * selection in list box.
364 * Parameters : hWnd - Handle of the dialog box
366 static void UpdateButtons(HWND hWnd)
368 BOOL sel = ListView_GetSelectedCount(GetDlgItem(hWnd, IDL_PROGRAMS)) != 0;
370 EnableWindow(GetDlgItem(hWnd, IDC_ADDREMOVE), sel);
371 EnableWindow(GetDlgItem(hWnd, IDC_SUPPORT_INFO), sel);
374 /* Definition of column headers for AddListViewColumns function */
375 typedef struct AppWizColumn {
381 AppWizColumn columns[] = {
382 {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
383 {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
384 {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
387 /******************************************************************************
388 * Name : AddListViewColumns
389 * Description: Adds column headers to the list view control.
390 * Parameters : hWnd - Handle of the list view control.
391 * Returns : TRUE if completed successfully, FALSE otherwise.
393 static BOOL AddListViewColumns(HWND hWnd)
395 WCHAR buf[MAX_STRING_LEN];
399 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
401 /* Add the columns */
402 for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
407 /* set width and format */
408 lvc.cx = columns[i].width;
409 lvc.fmt = columns[i].fmt;
411 LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
413 if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
420 /******************************************************************************
421 * Name : AddListViewImageList
422 * Description: Creates an ImageList for the list view control.
423 * Parameters : hWnd - Handle of the list view control.
424 * Returns : Handle of the image list.
426 static HIMAGELIST AddListViewImageList(HWND hWnd)
431 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
434 /* Add default icon to image list */
435 hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
436 ImageList_AddIcon(hSmall, hDefaultIcon);
437 DestroyIcon(hDefaultIcon);
439 (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
444 /******************************************************************************
445 * Name : ResetApplicationList
446 * Description: Empties the app list, if need be, and recreates it.
447 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
448 * hWnd - handle of the dialog box
449 * hImageList - handle of the image list
450 * Returns : New handle of the image list.
452 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
456 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
458 /* if first run, create the image list and add the listview columns */
461 if (!AddListViewColumns(hWndListView))
464 else /* we need to remove the existing things first */
466 RemoveItemsFromList(hWnd);
467 ImageList_Destroy(hImageList);
469 /* reset the list, since it's probably changed if the uninstallation was
474 /* now create the image list and add the applications to the listview */
475 hImageList = AddListViewImageList(hWndListView);
477 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
478 ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
480 AddApplicationsToList(hWndListView, hImageList);
486 /******************************************************************************
488 * Description: Callback procedure for main tab
489 * Parameters : hWnd - hWnd of the window
490 * msg - reason for calling function
491 * wParam - additional parameter
492 * lParam - additional parameter
493 * Returns : Dependant on message
495 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
497 static HIMAGELIST hImageList;
503 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
511 RemoveItemsFromList(hWnd);
512 ImageList_Destroy(hImageList);
519 nmh = (LPNMHDR) lParam;
526 case LVN_ITEMCHANGED:
539 /******************************************************************************
541 * Description: Main routine for applet
542 * Parameters : hWnd - hWnd of the Control Panel
544 static void StartApplet(HWND hWnd)
547 PROPSHEETHEADERW psh;
548 WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
550 /* Load the strings we will use */
551 LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
552 LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
554 /* Fill out the PROPSHEETPAGE */
555 psp.dwSize = sizeof (PROPSHEETPAGEW);
556 psp.dwFlags = PSP_USETITLE;
557 psp.hInstance = hInst;
558 psp.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
560 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
561 psp.pszTitle = tab_title;
564 /* Fill out the PROPSHEETHEADER */
565 psh.dwSize = sizeof (PROPSHEETHEADERW);
566 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
567 psh.hwndParent = hWnd;
568 psh.hInstance = hInst;
570 psh.pszCaption = app_title;
573 psh.pfnCallback = NULL;
576 /* Display the property sheet */
577 PropertySheetW (&psh);
580 /******************************************************************************
582 * Description: Entry point for Control Panel applets
583 * Parameters : hwndCPL - hWnd of the Control Panel
584 * message - reason for calling function
585 * lParam1 - additional parameter
586 * lParam2 - additional parameter
587 * Returns : Dependant on message
589 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
591 INITCOMMONCONTROLSEX iccEx;
596 iccEx.dwSize = sizeof(iccEx);
597 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
599 InitCommonControlsEx(&iccEx);
608 CPLINFO *appletInfo = (CPLINFO *) lParam2;
610 appletInfo->idIcon = ICO_MAIN;
611 appletInfo->idName = IDS_CPL_TITLE;
612 appletInfo->idInfo = IDS_CPL_DESC;
613 appletInfo->lData = 0;
619 StartApplet(hwndCPL);