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 ContactW[] = {'C','o','n','t','a','c','t',0};
82 static const WCHAR HelpLinkW[] = {'H','e','l','p','L','i','n','k',0};
83 static const WCHAR HelpTelephoneW[] = {'H','e','l','p','T','e','l','e','p','h',
85 static const WCHAR ReadmeW[] = {'R','e','a','d','m','e',0};
86 static const WCHAR URLUpdateInfoW[] = {'U','R','L','U','p','d','a','t','e','I',
88 static const WCHAR CommentsW[] = {'C','o','m','m','e','n','t','s',0};
89 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
90 'S','t','r','i','n','g',0};
92 static const WCHAR PathUninstallW[] = {
93 'S','o','f','t','w','a','r','e','\\',
94 'M','i','c','r','o','s','o','f','t','\\',
95 'W','i','n','d','o','w','s','\\',
96 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
97 'U','n','i','n','s','t','a','l','l',0 };
99 /******************************************************************************
101 * Description: Entry point for DLL file
103 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
106 TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
110 case DLL_PROCESS_ATTACH:
117 /******************************************************************************
119 * Description: Frees memory used by an AppInfo structure, and any children.
121 static void FreeAppInfo(APPINFO *info)
125 APPINFO *next_info = info->next;
127 HeapFree(GetProcessHeap(), 0, info->title);
128 HeapFree(GetProcessHeap(), 0, info->path);
129 HeapFree(GetProcessHeap(), 0, info->icon);
130 HeapFree(GetProcessHeap(), 0, info->publisher);
131 HeapFree(GetProcessHeap(), 0, info->version);
132 HeapFree(GetProcessHeap(), 0, info);
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 /******************************************************************************
375 * Name : UninstallProgram
376 * Description: Executes the specified program's installer.
377 * Parameters : id - the internal ID of the installer to remove
379 static void UninstallProgram(int id)
383 PROCESS_INFORMATION info;
384 WCHAR errormsg[MAX_STRING_LEN];
385 WCHAR sUninstallFailed[MAX_STRING_LEN];
389 LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
390 sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
392 for (iter = AppInfo; iter; iter = iter->next)
396 TRACE("Uninstalling %s (%s)\n", wine_dbgstr_w(iter->title),
397 wine_dbgstr_w(iter->path));
399 memset(&si, 0, sizeof(STARTUPINFOW));
400 si.cb = sizeof(STARTUPINFOW);
401 si.wShowWindow = SW_NORMAL;
402 res = CreateProcessW(NULL, iter->path, NULL, NULL, FALSE, 0, NULL,
407 CloseHandle(info.hThread);
409 /* wait for the process to exit */
410 WaitForSingleObject(info.hProcess, INFINITE);
411 CloseHandle(info.hProcess);
415 wsprintfW(errormsg, sUninstallFailed, iter->path);
417 if (MessageBoxW(0, errormsg, iter->title, MB_YESNO |
418 MB_ICONQUESTION) == IDYES)
420 /* delete the application's uninstall entry */
421 RegOpenKeyExW(iter->regroot, PathUninstallW, 0, KEY_READ, &hkey);
422 RegDeleteKeyW(hkey, iter->regkey);
432 /**********************************************************************************
433 * Name : SetInfoDialogText
434 * Description: Sets the text of a label in a window, based upon a registry entry
435 * or string passed to the function.
436 * Parameters : hKey - registry entry to read from, NULL if not reading
438 * lpKeyName - key to read from, or string to check if hKey is NULL
439 * lpAltMessage - alternative message if entry not found
440 * hWnd - handle of dialog box
441 * iDlgItem - ID of label in dialog box
443 static void SetInfoDialogText(HKEY hKey, LPWSTR lpKeyName, LPWSTR lpAltMessage,
444 HWND hWnd, int iDlgItem)
446 WCHAR buf[MAX_STRING_LEN];
450 hWndDlgItem = GetDlgItem(hWnd, iDlgItem);
452 /* if hKey is null, lpKeyName contains the string we want to check */
455 if ((lpKeyName) && (lstrlenW(lpKeyName) > 0))
456 SetWindowTextW(hWndDlgItem, lpKeyName);
458 SetWindowTextW(hWndDlgItem, lpAltMessage);
462 buflen = MAX_STRING_LEN;
464 if ((RegQueryValueExW(hKey, lpKeyName, 0, 0, (LPBYTE) buf, &buflen) ==
465 ERROR_SUCCESS) && (lstrlenW(buf) > 0))
466 SetWindowTextW(hWndDlgItem, buf);
468 SetWindowTextW(hWndDlgItem, lpAltMessage);
472 /******************************************************************************
473 * Name : SupportInfoDlgProc
474 * Description: Callback procedure for support info dialog
475 * Parameters : hWnd - hWnd of the window
476 * msg - reason for calling function
477 * wParam - additional parameter
478 * lParam - additional parameter
479 * Returns : Dependant on message
481 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
485 WCHAR oldtitle[MAX_STRING_LEN];
486 WCHAR buf[MAX_STRING_LEN];
487 WCHAR key[MAX_STRING_LEN];
488 WCHAR notfound[MAX_STRING_LEN];
493 for (iter = AppInfo; iter; iter = iter->next)
495 if (iter->id == (int) lParam)
497 lstrcpyW(key, PathUninstallW);
498 lstrcatW(key, BackSlashW);
499 lstrcatW(key, iter->regkey);
501 /* check the application's registry entries */
502 RegOpenKeyExW(iter->regroot, key, 0, KEY_READ, &hkey);
504 /* Load our "not specified" string */
505 LoadStringW(hInst, IDS_NOT_SPECIFIED, notfound,
506 sizeof(notfound) / sizeof(notfound[0]));
508 /* Update the data for items already read into the structure */
509 SetInfoDialogText(NULL, iter->publisher, notfound, hWnd,
511 SetInfoDialogText(NULL, iter->version, notfound, hWnd,
514 /* And now update the data for those items in the registry */
515 SetInfoDialogText(hkey, (LPWSTR) ContactW, notfound, hWnd,
517 SetInfoDialogText(hkey, (LPWSTR) HelpLinkW, notfound, hWnd,
519 SetInfoDialogText(hkey, (LPWSTR) HelpTelephoneW, notfound, hWnd,
521 SetInfoDialogText(hkey, (LPWSTR) ReadmeW, notfound, hWnd,
523 SetInfoDialogText(hkey, (LPWSTR) URLUpdateInfoW, notfound, hWnd,
525 SetInfoDialogText(hkey, (LPWSTR) CommentsW, notfound, hWnd,
528 /* Update the main label with the app name */
529 if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
530 MAX_STRING_LEN) != 0)
532 wsprintfW(buf, oldtitle, iter->title);
533 SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
548 switch (LOWORD(wParam))
551 EndDialog(hWnd, TRUE);
562 /******************************************************************************
564 * Description: Displays the Support Information dialog
565 * Parameters : hWnd - Handle of the main dialog
566 * id - ID of the application to display information for
568 static void SupportInfo(HWND hWnd, int id)
570 DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
571 SupportInfoDlgProc, (LPARAM) id);
574 /* Definition of column headers for AddListViewColumns function */
575 typedef struct AppWizColumn {
581 AppWizColumn columns[] = {
582 {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
583 {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
584 {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
587 /******************************************************************************
588 * Name : AddListViewColumns
589 * Description: Adds column headers to the list view control.
590 * Parameters : hWnd - Handle of the list view control.
591 * Returns : TRUE if completed successfully, FALSE otherwise.
593 static BOOL AddListViewColumns(HWND hWnd)
595 WCHAR buf[MAX_STRING_LEN];
599 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
601 /* Add the columns */
602 for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
607 /* set width and format */
608 lvc.cx = columns[i].width;
609 lvc.fmt = columns[i].fmt;
611 LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
613 if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
620 /******************************************************************************
621 * Name : AddListViewImageList
622 * Description: Creates an ImageList for the list view control.
623 * Parameters : hWnd - Handle of the list view control.
624 * Returns : Handle of the image list.
626 static HIMAGELIST AddListViewImageList(HWND hWnd)
631 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
634 /* Add default icon to image list */
635 hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
636 ImageList_AddIcon(hSmall, hDefaultIcon);
637 DestroyIcon(hDefaultIcon);
639 (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
644 /******************************************************************************
645 * Name : ResetApplicationList
646 * Description: Empties the app list, if need be, and recreates it.
647 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
648 * hWnd - handle of the dialog box
649 * hImageList - handle of the image list
650 * Returns : New handle of the image list.
652 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
656 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
658 /* if first run, create the image list and add the listview columns */
661 if (!AddListViewColumns(hWndListView))
664 else /* we need to remove the existing things first */
666 RemoveItemsFromList(hWnd);
667 ImageList_Destroy(hImageList);
669 /* reset the list, since it's probably changed if the uninstallation was
674 /* now create the image list and add the applications to the listview */
675 hImageList = AddListViewImageList(hWndListView);
677 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
678 ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
680 AddApplicationsToList(hWndListView, hImageList);
686 /******************************************************************************
688 * Description: Callback procedure for main tab
689 * Parameters : hWnd - hWnd of the window
690 * msg - reason for calling function
691 * wParam - additional parameter
692 * lParam - additional parameter
693 * Returns : Dependant on message
695 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
698 static HIMAGELIST hImageList;
705 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
713 RemoveItemsFromList(hWnd);
714 ImageList_Destroy(hImageList);
721 nmh = (LPNMHDR) lParam;
728 case LVN_ITEMCHANGED:
738 switch (LOWORD(wParam))
741 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
742 LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
746 lvItem.iItem = selitem;
747 lvItem.mask = LVIF_PARAM;
749 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
750 0, (LPARAM) &lvItem))
751 UninstallProgram(lvItem.lParam);
754 hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
758 case IDC_SUPPORT_INFO:
759 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
760 LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
764 lvItem.iItem = selitem;
765 lvItem.mask = LVIF_PARAM;
767 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
768 0, (LPARAM) &lvItem))
769 SupportInfo(hWnd, lvItem.lParam);
781 /******************************************************************************
783 * Description: Main routine for applet
784 * Parameters : hWnd - hWnd of the Control Panel
786 static void StartApplet(HWND hWnd)
789 PROPSHEETHEADERW psh;
790 WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
792 /* Load the strings we will use */
793 LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
794 LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
796 /* Fill out the PROPSHEETPAGE */
797 psp.dwSize = sizeof (PROPSHEETPAGEW);
798 psp.dwFlags = PSP_USETITLE;
799 psp.hInstance = hInst;
800 psp.u.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
801 psp.u2.pszIcon = NULL;
802 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
803 psp.pszTitle = tab_title;
806 /* Fill out the PROPSHEETHEADER */
807 psh.dwSize = sizeof (PROPSHEETHEADERW);
808 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
809 psh.hwndParent = hWnd;
810 psh.hInstance = hInst;
811 psh.u.pszIcon = NULL;
812 psh.pszCaption = app_title;
815 psh.pfnCallback = NULL;
816 psh.u2.nStartPage = 0;
818 /* Display the property sheet */
819 PropertySheetW (&psh);
822 /******************************************************************************
824 * Description: Entry point for Control Panel applets
825 * Parameters : hwndCPL - hWnd of the Control Panel
826 * message - reason for calling function
827 * lParam1 - additional parameter
828 * lParam2 - additional parameter
829 * Returns : Dependant on message
831 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
833 INITCOMMONCONTROLSEX iccEx;
838 iccEx.dwSize = sizeof(iccEx);
839 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
841 InitCommonControlsEx(&iccEx);
850 CPLINFO *appletInfo = (CPLINFO *) lParam2;
852 appletInfo->idIcon = ICO_MAIN;
853 appletInfo->idName = IDS_CPL_TITLE;
854 appletInfo->idInfo = IDS_CPL_DESC;
855 appletInfo->lData = 0;
861 StartApplet(hwndCPL);