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 /* wait for the process to exit */
408 WaitForSingleObject(info.hProcess, INFINITE);
412 wsprintfW(errormsg, sUninstallFailed, iter->path);
414 if (MessageBoxW(0, errormsg, iter->title, MB_YESNO |
415 MB_ICONQUESTION) == IDYES)
417 /* delete the application's uninstall entry */
418 RegOpenKeyExW(iter->regroot, PathUninstallW, 0, KEY_READ, &hkey);
419 RegDeleteKeyW(hkey, iter->regkey);
429 /**********************************************************************************
430 * Name : SetInfoDialogText
431 * Description: Sets the text of a label in a window, based upon a registry entry
432 * or string passed to the function.
433 * Parameters : hKey - registry entry to read from, NULL if not reading
435 * lpKeyName - key to read from, or string to check if hKey is NULL
436 * lpAltMessage - alternative message if entry not found
437 * hWnd - handle of dialog box
438 * iDlgItem - ID of label in dialog box
440 static void SetInfoDialogText(HKEY hKey, LPWSTR lpKeyName, LPWSTR lpAltMessage,
441 HWND hWnd, int iDlgItem)
443 WCHAR buf[MAX_STRING_LEN];
447 hWndDlgItem = GetDlgItem(hWnd, iDlgItem);
449 /* if hKey is null, lpKeyName contains the string we want to check */
452 if ((lpKeyName) && (lstrlenW(lpKeyName) > 0))
453 SetWindowTextW(hWndDlgItem, lpKeyName);
455 SetWindowTextW(hWndDlgItem, lpAltMessage);
459 buflen = MAX_STRING_LEN;
461 if ((RegQueryValueExW(hKey, lpKeyName, 0, 0, (LPBYTE) buf, &buflen) ==
462 ERROR_SUCCESS) && (lstrlenW(buf) > 0))
463 SetWindowTextW(hWndDlgItem, buf);
465 SetWindowTextW(hWndDlgItem, lpAltMessage);
469 /******************************************************************************
470 * Name : SupportInfoDlgProc
471 * Description: Callback procedure for support info dialog
472 * Parameters : hWnd - hWnd of the window
473 * msg - reason for calling function
474 * wParam - additional parameter
475 * lParam - additional parameter
476 * Returns : Dependant on message
478 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
482 WCHAR oldtitle[MAX_STRING_LEN];
483 WCHAR buf[MAX_STRING_LEN];
484 WCHAR key[MAX_STRING_LEN];
485 WCHAR notfound[MAX_STRING_LEN];
490 for (iter = AppInfo; iter; iter = iter->next)
492 if (iter->id == (int) lParam)
494 lstrcpyW(key, PathUninstallW);
495 lstrcatW(key, BackSlashW);
496 lstrcatW(key, iter->regkey);
498 /* check the application's registry entries */
499 RegOpenKeyExW(iter->regroot, key, 0, KEY_READ, &hkey);
501 /* Load our "not specified" string */
502 LoadStringW(hInst, IDS_NOT_SPECIFIED, notfound,
503 sizeof(notfound) / sizeof(notfound[0]));
505 /* Update the data for items already read into the structure */
506 SetInfoDialogText(NULL, iter->publisher, notfound, hWnd,
508 SetInfoDialogText(NULL, iter->version, notfound, hWnd,
511 /* And now update the data for those items in the registry */
512 SetInfoDialogText(hkey, (LPWSTR) ContactW, notfound, hWnd,
514 SetInfoDialogText(hkey, (LPWSTR) HelpLinkW, notfound, hWnd,
516 SetInfoDialogText(hkey, (LPWSTR) HelpTelephoneW, notfound, hWnd,
518 SetInfoDialogText(hkey, (LPWSTR) ReadmeW, notfound, hWnd,
520 SetInfoDialogText(hkey, (LPWSTR) URLUpdateInfoW, notfound, hWnd,
522 SetInfoDialogText(hkey, (LPWSTR) CommentsW, notfound, hWnd,
525 /* Update the main label with the app name */
526 if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
527 MAX_STRING_LEN) != 0)
529 wsprintfW(buf, oldtitle, iter->title);
530 SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
545 switch (LOWORD(wParam))
548 EndDialog(hWnd, TRUE);
559 /******************************************************************************
561 * Description: Displays the Support Information dialog
562 * Parameters : hWnd - Handle of the main dialog
563 * id - ID of the application to display information for
565 static void SupportInfo(HWND hWnd, int id)
567 DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
568 SupportInfoDlgProc, (LPARAM) id);
571 /* Definition of column headers for AddListViewColumns function */
572 typedef struct AppWizColumn {
578 AppWizColumn columns[] = {
579 {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
580 {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
581 {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
584 /******************************************************************************
585 * Name : AddListViewColumns
586 * Description: Adds column headers to the list view control.
587 * Parameters : hWnd - Handle of the list view control.
588 * Returns : TRUE if completed successfully, FALSE otherwise.
590 static BOOL AddListViewColumns(HWND hWnd)
592 WCHAR buf[MAX_STRING_LEN];
596 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
598 /* Add the columns */
599 for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
604 /* set width and format */
605 lvc.cx = columns[i].width;
606 lvc.fmt = columns[i].fmt;
608 LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
610 if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
617 /******************************************************************************
618 * Name : AddListViewImageList
619 * Description: Creates an ImageList for the list view control.
620 * Parameters : hWnd - Handle of the list view control.
621 * Returns : Handle of the image list.
623 static HIMAGELIST AddListViewImageList(HWND hWnd)
628 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
631 /* Add default icon to image list */
632 hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
633 ImageList_AddIcon(hSmall, hDefaultIcon);
634 DestroyIcon(hDefaultIcon);
636 (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
641 /******************************************************************************
642 * Name : ResetApplicationList
643 * Description: Empties the app list, if need be, and recreates it.
644 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
645 * hWnd - handle of the dialog box
646 * hImageList - handle of the image list
647 * Returns : New handle of the image list.
649 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
653 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
655 /* if first run, create the image list and add the listview columns */
658 if (!AddListViewColumns(hWndListView))
661 else /* we need to remove the existing things first */
663 RemoveItemsFromList(hWnd);
664 ImageList_Destroy(hImageList);
666 /* reset the list, since it's probably changed if the uninstallation was
671 /* now create the image list and add the applications to the listview */
672 hImageList = AddListViewImageList(hWndListView);
674 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
675 ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
677 AddApplicationsToList(hWndListView, hImageList);
683 /******************************************************************************
685 * Description: Callback procedure for main tab
686 * Parameters : hWnd - hWnd of the window
687 * msg - reason for calling function
688 * wParam - additional parameter
689 * lParam - additional parameter
690 * Returns : Dependant on message
692 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
695 static HIMAGELIST hImageList;
702 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
710 RemoveItemsFromList(hWnd);
711 ImageList_Destroy(hImageList);
718 nmh = (LPNMHDR) lParam;
725 case LVN_ITEMCHANGED:
735 switch (LOWORD(wParam))
738 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
739 LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
743 lvItem.iItem = selitem;
744 lvItem.mask = LVIF_PARAM;
746 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
747 0, (LPARAM) &lvItem))
748 UninstallProgram(lvItem.lParam);
751 hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
755 case IDC_SUPPORT_INFO:
756 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
757 LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
761 lvItem.iItem = selitem;
762 lvItem.mask = LVIF_PARAM;
764 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
765 0, (LPARAM) &lvItem))
766 SupportInfo(hWnd, lvItem.lParam);
778 /******************************************************************************
780 * Description: Main routine for applet
781 * Parameters : hWnd - hWnd of the Control Panel
783 static void StartApplet(HWND hWnd)
786 PROPSHEETHEADERW psh;
787 WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
789 /* Load the strings we will use */
790 LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
791 LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
793 /* Fill out the PROPSHEETPAGE */
794 psp.dwSize = sizeof (PROPSHEETPAGEW);
795 psp.dwFlags = PSP_USETITLE;
796 psp.hInstance = hInst;
797 psp.u.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
798 psp.u2.pszIcon = NULL;
799 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
800 psp.pszTitle = tab_title;
803 /* Fill out the PROPSHEETHEADER */
804 psh.dwSize = sizeof (PROPSHEETHEADERW);
805 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
806 psh.hwndParent = hWnd;
807 psh.hInstance = hInst;
808 psh.u.pszIcon = NULL;
809 psh.pszCaption = app_title;
812 psh.pfnCallback = NULL;
813 psh.u2.nStartPage = 0;
815 /* Display the property sheet */
816 PropertySheetW (&psh);
819 /******************************************************************************
821 * Description: Entry point for Control Panel applets
822 * Parameters : hwndCPL - hWnd of the Control Panel
823 * message - reason for calling function
824 * lParam1 - additional parameter
825 * lParam2 - additional parameter
826 * Returns : Dependant on message
828 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
830 INITCOMMONCONTROLSEX iccEx;
835 iccEx.dwSize = sizeof(iccEx);
836 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
838 InitCommonControlsEx(&iccEx);
847 CPLINFO *appletInfo = (CPLINFO *) lParam2;
849 appletInfo->idIcon = ICO_MAIN;
850 appletInfo->idName = IDS_CPL_TITLE;
851 appletInfo->idInfo = IDS_CPL_DESC;
852 appletInfo->lData = 0;
858 StartApplet(hwndCPL);