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
26 #define NONAMELESSUNION
29 #include "wine/port.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl);
50 /* define a maximum length for various buffers we use */
51 #define MAX_STRING_LEN 1024
53 typedef struct APPINFO {
66 WCHAR regkey[MAX_STRING_LEN];
71 static struct APPINFO *AppInfo = NULL;
72 static HINSTANCE hInst;
74 /* names of registry keys */
75 static const WCHAR BackSlashW[] = { '\\', 0 };
76 static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
77 static const WCHAR DisplayIconW[] = {'D','i','s','p','l','a','y','I','c','o','n',0};
78 static const WCHAR DisplayVersionW[] = {'D','i','s','p','l','a','y','V','e','r',
80 static const WCHAR PublisherW[] = {'P','u','b','l','i','s','h','e','r',0};
81 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
82 'S','t','r','i','n','g',0};
84 static const WCHAR PathUninstallW[] = {
85 'S','o','f','t','w','a','r','e','\\',
86 'M','i','c','r','o','s','o','f','t','\\',
87 'W','i','n','d','o','w','s','\\',
88 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
89 'U','n','i','n','s','t','a','l','l',0 };
91 /******************************************************************************
93 * Description: Entry point for DLL file
95 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
98 TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
102 case DLL_PROCESS_ATTACH:
109 /******************************************************************************
111 * Description: Frees memory used by an AppInfo structure, and any children.
113 static void FreeAppInfo(APPINFO *info)
117 APPINFO *next_info = info->next;
119 HeapFree(GetProcessHeap(), 0, info->title);
120 HeapFree(GetProcessHeap(), 0, info->path);
121 HeapFree(GetProcessHeap(), 0, info->icon);
122 HeapFree(GetProcessHeap(), 0, info->publisher);
123 HeapFree(GetProcessHeap(), 0, info->version);
124 HeapFree(GetProcessHeap(), 0, info);
129 /******************************************************************************
130 * Name : ReadApplicationsFromRegistry
131 * Description: Creates a linked list of uninstallable applications from the
133 * Parameters : root - Which registry root to read from (HKCU/HKLM)
134 * Returns : TRUE if successful, FALSE otherwise
136 static BOOL ReadApplicationsFromRegistry(HKEY root)
138 HKEY hkeyUninst, hkeyApp;
140 DWORD sizeOfSubKeyName, displen, uninstlen;
141 WCHAR subKeyName[256];
142 WCHAR key_app[MAX_STRING_LEN];
144 APPINFO *iter = AppInfo;
148 if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
152 lstrcpyW(key_app, PathUninstallW);
153 lstrcatW(key_app, BackSlashW);
154 p = key_app+lstrlenW(PathUninstallW)+1;
156 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
160 /* find the end of the list */
161 for (iter = AppInfo; iter->next; iter = iter->next);
164 for (i = 0; RegEnumKeyExW(hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL,
165 NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; ++i)
167 lstrcpyW(p, subKeyName);
168 RegOpenKeyExW(root, key_app, 0, KEY_READ, &hkeyApp);
173 if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) ==
174 ERROR_SUCCESS) && (RegQueryValueExW(hkeyApp, UninstallCommandlineW,
175 0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
177 /* if we already have iter, allocate the next entry */
180 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
181 sizeof(struct APPINFO));
190 /* if not, start the list */
191 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
192 sizeof(struct APPINFO));
198 iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
203 RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
206 /* now get DisplayIcon */
208 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
214 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
219 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
222 /* separate the index from the icon name, if supplied */
223 iconPtr = strchrW(iter->icon, ',');
228 iter->iconIdx = atoiW(iconPtr);
232 iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
237 RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
238 (LPBYTE)iter->path, &uninstlen);
240 /* publisher, version */
241 if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
244 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
246 if (!iter->publisher)
249 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
253 if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
256 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
261 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
266 iter->regroot = root;
267 lstrcpyW(iter->regkey, subKeyName);
272 RegCloseKey(hkeyApp);
273 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
280 RegCloseKey(hkeyApp);
284 RegCloseKey(hkeyUninst);
289 /******************************************************************************
290 * Name : AddApplicationsToList
291 * Description: Populates the list box with applications.
292 * Parameters : hWnd - Handle of the dialog box
294 static void AddApplicationsToList(HWND hWnd, HIMAGELIST hList)
296 APPINFO *iter = AppInfo;
308 if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
310 index = ImageList_AddIcon(hList, hIcon);
315 lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
316 lvItem.iItem = iter->id;
318 lvItem.pszText = iter->title;
319 lvItem.iImage = index;
320 lvItem.lParam = iter->id;
322 index = ListView_InsertItemW(hWnd, &lvItem);
324 /* now add the subitems (columns) */
325 ListView_SetItemTextW(hWnd, index, 1, iter->publisher);
326 ListView_SetItemTextW(hWnd, index, 2, iter->version);
332 /******************************************************************************
333 * Name : RemoveItemsFromList
334 * Description: Clears the application list box.
335 * Parameters : hWnd - Handle of the dialog box
337 static void RemoveItemsFromList(HWND hWnd)
339 SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_DELETEALLITEMS, 0, 0);
342 /******************************************************************************
344 * Description: Frees memory used by the application linked list.
346 static inline void EmptyList(void)
348 FreeAppInfo(AppInfo);
352 /******************************************************************************
353 * Name : UpdateButtons
354 * Description: Enables/disables the Add/Remove button depending on current
355 * selection in list box.
356 * Parameters : hWnd - Handle of the dialog box
358 static void UpdateButtons(HWND hWnd)
360 BOOL sel = ListView_GetSelectedCount(GetDlgItem(hWnd, IDL_PROGRAMS)) != 0;
362 EnableWindow(GetDlgItem(hWnd, IDC_ADDREMOVE), sel);
363 EnableWindow(GetDlgItem(hWnd, IDC_SUPPORT_INFO), sel);
366 /******************************************************************************
367 * Name : UninstallProgram
368 * Description: Executes the specified program's installer.
369 * Parameters : id - the internal ID of the installer to remove
371 static void UninstallProgram(int id)
375 PROCESS_INFORMATION info;
376 WCHAR errormsg[MAX_STRING_LEN];
377 WCHAR sUninstallFailed[MAX_STRING_LEN];
381 LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
382 sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
384 for (iter = AppInfo; iter; iter = iter->next)
388 TRACE("Uninstalling %s (%s)\n", wine_dbgstr_w(iter->title),
389 wine_dbgstr_w(iter->path));
391 memset(&si, 0, sizeof(STARTUPINFOW));
392 si.cb = sizeof(STARTUPINFOW);
393 si.wShowWindow = SW_NORMAL;
394 res = CreateProcessW(NULL, iter->path, NULL, NULL, FALSE, 0, NULL,
399 /* wait for the process to exit */
400 WaitForSingleObject(info.hProcess, INFINITE);
404 wsprintfW(errormsg, sUninstallFailed, iter->path);
406 if (MessageBoxW(0, errormsg, iter->title, MB_YESNO |
407 MB_ICONQUESTION) == IDYES)
409 /* delete the application's uninstall entry */
410 RegOpenKeyExW(iter->regroot, PathUninstallW, 0, KEY_READ, &hkey);
411 RegDeleteKeyW(hkey, iter->regkey);
421 /******************************************************************************
422 * Name : SupportInfoDlgProc
423 * Description: Callback procedure for support info dialog
424 * Parameters : hWnd - hWnd of the window
425 * msg - reason for calling function
426 * wParam - additional parameter
427 * lParam - additional parameter
428 * Returns : Dependant on message
430 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
433 WCHAR oldtitle[MAX_STRING_LEN];
434 WCHAR buf[MAX_STRING_LEN];
439 for (iter = AppInfo; iter; iter = iter->next)
441 if (iter->id == (int) lParam)
443 /* Update the main label with the app name */
444 if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
445 MAX_STRING_LEN) != 0)
447 wsprintfW(buf, oldtitle, iter->title);
448 SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
461 switch (LOWORD(wParam))
464 EndDialog(hWnd, TRUE);
475 /******************************************************************************
477 * Description: Displays the Support Information dialog
478 * Parameters : hWnd - Handle of the main dialog
479 * id - ID of the application to display information for
481 static void SupportInfo(HWND hWnd, int id)
483 DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
484 SupportInfoDlgProc, (LPARAM) id);
487 /* Definition of column headers for AddListViewColumns function */
488 typedef struct AppWizColumn {
494 AppWizColumn columns[] = {
495 {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
496 {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
497 {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
500 /******************************************************************************
501 * Name : AddListViewColumns
502 * Description: Adds column headers to the list view control.
503 * Parameters : hWnd - Handle of the list view control.
504 * Returns : TRUE if completed successfully, FALSE otherwise.
506 static BOOL AddListViewColumns(HWND hWnd)
508 WCHAR buf[MAX_STRING_LEN];
512 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
514 /* Add the columns */
515 for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
520 /* set width and format */
521 lvc.cx = columns[i].width;
522 lvc.fmt = columns[i].fmt;
524 LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
526 if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
533 /******************************************************************************
534 * Name : AddListViewImageList
535 * Description: Creates an ImageList for the list view control.
536 * Parameters : hWnd - Handle of the list view control.
537 * Returns : Handle of the image list.
539 static HIMAGELIST AddListViewImageList(HWND hWnd)
544 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
547 /* Add default icon to image list */
548 hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
549 ImageList_AddIcon(hSmall, hDefaultIcon);
550 DestroyIcon(hDefaultIcon);
552 (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
557 /******************************************************************************
558 * Name : ResetApplicationList
559 * Description: Empties the app list, if need be, and recreates it.
560 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
561 * hWnd - handle of the dialog box
562 * hImageList - handle of the image list
563 * Returns : New handle of the image list.
565 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
569 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
571 /* if first run, create the image list and add the listview columns */
574 if (!AddListViewColumns(hWndListView))
577 else /* we need to remove the existing things first */
579 RemoveItemsFromList(hWnd);
580 ImageList_Destroy(hImageList);
582 /* reset the list, since it's probably changed if the uninstallation was
587 /* now create the image list and add the applications to the listview */
588 hImageList = AddListViewImageList(hWndListView);
590 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
591 ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
593 AddApplicationsToList(hWndListView, hImageList);
599 /******************************************************************************
601 * Description: Callback procedure for main tab
602 * Parameters : hWnd - hWnd of the window
603 * msg - reason for calling function
604 * wParam - additional parameter
605 * lParam - additional parameter
606 * Returns : Dependant on message
608 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
611 static HIMAGELIST hImageList;
618 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
626 RemoveItemsFromList(hWnd);
627 ImageList_Destroy(hImageList);
634 nmh = (LPNMHDR) lParam;
641 case LVN_ITEMCHANGED:
651 switch (LOWORD(wParam))
654 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
655 LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
659 lvItem.iItem = selitem;
660 lvItem.mask = LVIF_PARAM;
662 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
663 0, (LPARAM) &lvItem))
664 UninstallProgram(lvItem.lParam);
667 hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
671 case IDC_SUPPORT_INFO:
672 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
673 LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
677 lvItem.iItem = selitem;
678 lvItem.mask = LVIF_PARAM;
680 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
681 0, (LPARAM) &lvItem))
682 SupportInfo(hWnd, lvItem.lParam);
694 /******************************************************************************
696 * Description: Main routine for applet
697 * Parameters : hWnd - hWnd of the Control Panel
699 static void StartApplet(HWND hWnd)
702 PROPSHEETHEADERW psh;
703 WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
705 /* Load the strings we will use */
706 LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
707 LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
709 /* Fill out the PROPSHEETPAGE */
710 psp.dwSize = sizeof (PROPSHEETPAGEW);
711 psp.dwFlags = PSP_USETITLE;
712 psp.hInstance = hInst;
713 psp.u.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
714 psp.u2.pszIcon = NULL;
715 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
716 psp.pszTitle = tab_title;
719 /* Fill out the PROPSHEETHEADER */
720 psh.dwSize = sizeof (PROPSHEETHEADERW);
721 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
722 psh.hwndParent = hWnd;
723 psh.hInstance = hInst;
724 psh.u.pszIcon = NULL;
725 psh.pszCaption = app_title;
728 psh.pfnCallback = NULL;
729 psh.u2.nStartPage = 0;
731 /* Display the property sheet */
732 PropertySheetW (&psh);
735 /******************************************************************************
737 * Description: Entry point for Control Panel applets
738 * Parameters : hwndCPL - hWnd of the Control Panel
739 * message - reason for calling function
740 * lParam1 - additional parameter
741 * lParam2 - additional parameter
742 * Returns : Dependant on message
744 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
746 INITCOMMONCONTROLSEX iccEx;
751 iccEx.dwSize = sizeof(iccEx);
752 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
754 InitCommonControlsEx(&iccEx);
763 CPLINFO *appletInfo = (CPLINFO *) lParam2;
765 appletInfo->idIcon = ICO_MAIN;
766 appletInfo->idName = IDS_CPL_TITLE;
767 appletInfo->idInfo = IDS_CPL_DESC;
768 appletInfo->lData = 0;
774 StartApplet(hwndCPL);