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
)
115 APPINFO
*next_info
= info
->next
;
117 HeapFree(GetProcessHeap(), 0, info
->title
);
118 HeapFree(GetProcessHeap(), 0, info
->path
);
119 HeapFree(GetProcessHeap(), 0, info
->icon
);
120 HeapFree(GetProcessHeap(), 0, info
->publisher
);
121 HeapFree(GetProcessHeap(), 0, info
->version
);
122 HeapFree(GetProcessHeap(), 0, info
);
127 /******************************************************************************
128 * Name : ReadApplicationsFromRegistry
129 * Description: Creates a linked list of uninstallable applications from the
131 * Parameters : root - Which registry root to read from (HKCU/HKLM)
132 * Returns : TRUE if successful, FALSE otherwise
134 static BOOL
ReadApplicationsFromRegistry(HKEY root
)
136 HKEY hkeyUninst
, hkeyApp
;
138 DWORD sizeOfSubKeyName
, displen
, uninstlen
;
139 WCHAR subKeyName
[256];
140 WCHAR key_app
[MAX_STRING_LEN
];
142 APPINFO
*iter
= AppInfo
;
146 if (RegOpenKeyExW(root
, PathUninstallW
, 0, KEY_READ
, &hkeyUninst
) !=
150 lstrcpyW(key_app
, PathUninstallW
);
151 lstrcatW(key_app
, BackSlashW
);
152 p
= key_app
+lstrlenW(PathUninstallW
)+1;
154 sizeOfSubKeyName
= sizeof(subKeyName
) / sizeof(subKeyName
[0]);
158 /* find the end of the list */
159 for (iter
= AppInfo
; iter
->next
; iter
= iter
->next
);
162 for (i
= 0; RegEnumKeyExW(hkeyUninst
, i
, subKeyName
, &sizeOfSubKeyName
, NULL
,
163 NULL
, NULL
, NULL
) != ERROR_NO_MORE_ITEMS
; ++i
)
165 lstrcpyW(p
, subKeyName
);
166 RegOpenKeyExW(root
, key_app
, 0, KEY_READ
, &hkeyApp
);
171 if ((RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0, NULL
, &displen
) ==
172 ERROR_SUCCESS
) && (RegQueryValueExW(hkeyApp
, UninstallCommandlineW
,
173 0, 0, NULL
, &uninstlen
) == ERROR_SUCCESS
))
175 /* if we already have iter, allocate the next entry */
178 iter
->next
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
179 sizeof(struct APPINFO
));
188 /* if not, start the list */
189 iter
= AppInfo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
190 sizeof(struct APPINFO
));
196 iter
->title
= HeapAlloc(GetProcessHeap(), 0, displen
);
201 RegQueryValueExW(hkeyApp
, DisplayNameW
, 0, 0, (LPBYTE
)iter
->title
,
204 /* now get DisplayIcon */
206 RegQueryValueExW(hkeyApp
, DisplayIconW
, 0, 0, NULL
, &displen
);
212 iter
->icon
= HeapAlloc(GetProcessHeap(), 0, displen
);
217 RegQueryValueExW(hkeyApp
, DisplayIconW
, 0, 0, (LPBYTE
)iter
->icon
,
220 /* separate the index from the icon name, if supplied */
221 iconPtr
= strchrW(iter
->icon
, ',');
226 iter
->iconIdx
= atoiW(iconPtr
);
230 iter
->path
= HeapAlloc(GetProcessHeap(), 0, uninstlen
);
235 RegQueryValueExW(hkeyApp
, UninstallCommandlineW
, 0, 0,
236 (LPBYTE
)iter
->path
, &uninstlen
);
238 /* publisher, version */
239 if (RegQueryValueExW(hkeyApp
, PublisherW
, 0, 0, NULL
, &displen
) ==
242 iter
->publisher
= HeapAlloc(GetProcessHeap(), 0, displen
);
244 if (!iter
->publisher
)
247 RegQueryValueExW(hkeyApp
, PublisherW
, 0, 0, (LPBYTE
)iter
->publisher
,
251 if (RegQueryValueExW(hkeyApp
, DisplayVersionW
, 0, 0, NULL
, &displen
) ==
254 iter
->version
= HeapAlloc(GetProcessHeap(), 0, displen
);
259 RegQueryValueExW(hkeyApp
, DisplayVersionW
, 0, 0, (LPBYTE
)iter
->version
,
264 iter
->regroot
= root
;
265 lstrcpyW(iter
->regkey
, subKeyName
);
270 RegCloseKey(hkeyApp
);
271 sizeOfSubKeyName
= sizeof(subKeyName
) / sizeof(subKeyName
[0]);
278 RegCloseKey(hkeyApp
);
282 RegCloseKey(hkeyUninst
);
287 /******************************************************************************
288 * Name : AddApplicationsToList
289 * Description: Populates the list box with applications.
290 * Parameters : hWnd - Handle of the dialog box
292 static void AddApplicationsToList(HWND hWnd
, HIMAGELIST hList
)
294 APPINFO
*iter
= AppInfo
;
306 if (ExtractIconExW(iter
->icon
, iter
->iconIdx
, NULL
, &hIcon
, 1) == 1)
308 index
= ImageList_AddIcon(hList
, hIcon
);
313 lvItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
314 lvItem
.iItem
= iter
->id
;
316 lvItem
.pszText
= iter
->title
;
317 lvItem
.iImage
= index
;
318 lvItem
.lParam
= iter
->id
;
320 index
= ListView_InsertItemW(hWnd
, &lvItem
);
322 /* now add the subitems (columns) */
323 ListView_SetItemTextW(hWnd
, index
, 1, iter
->publisher
);
324 ListView_SetItemTextW(hWnd
, index
, 2, iter
->version
);
330 /******************************************************************************
331 * Name : RemoveItemsFromList
332 * Description: Clears the application list box.
333 * Parameters : hWnd - Handle of the dialog box
335 static void RemoveItemsFromList(HWND hWnd
)
337 SendDlgItemMessageW(hWnd
, IDL_PROGRAMS
, LVM_DELETEALLITEMS
, 0, 0);
340 /******************************************************************************
342 * Description: Frees memory used by the application linked list.
344 static inline void EmptyList(void)
346 FreeAppInfo(AppInfo
);
350 /******************************************************************************
351 * Name : UpdateButtons
352 * Description: Enables/disables the Add/Remove button depending on current
353 * selection in list box.
354 * Parameters : hWnd - Handle of the dialog box
356 static void UpdateButtons(HWND hWnd
)
358 BOOL sel
= ListView_GetSelectedCount(GetDlgItem(hWnd
, IDL_PROGRAMS
)) != 0;
360 EnableWindow(GetDlgItem(hWnd
, IDC_ADDREMOVE
), sel
);
361 EnableWindow(GetDlgItem(hWnd
, IDC_SUPPORT_INFO
), sel
);
364 /******************************************************************************
365 * Name : UninstallProgram
366 * Description: Executes the specified program's installer.
367 * Parameters : id - the internal ID of the installer to remove
369 static void UninstallProgram(int id
)
373 PROCESS_INFORMATION info
;
374 WCHAR errormsg
[MAX_STRING_LEN
];
375 WCHAR sUninstallFailed
[MAX_STRING_LEN
];
379 LoadStringW(hInst
, IDS_UNINSTALL_FAILED
, sUninstallFailed
,
380 sizeof(sUninstallFailed
) / sizeof(sUninstallFailed
[0]));
382 for (iter
= AppInfo
; iter
; iter
= iter
->next
)
386 TRACE("Uninstalling %s (%s)\n", wine_dbgstr_w(iter
->title
),
387 wine_dbgstr_w(iter
->path
));
389 memset(&si
, 0, sizeof(STARTUPINFOW
));
390 si
.cb
= sizeof(STARTUPINFOW
);
391 si
.wShowWindow
= SW_NORMAL
;
392 res
= CreateProcessW(NULL
, iter
->path
, NULL
, NULL
, FALSE
, 0, NULL
,
397 /* wait for the process to exit */
398 WaitForSingleObject(info
.hProcess
, INFINITE
);
402 wsprintfW(errormsg
, sUninstallFailed
, iter
->path
);
404 if (MessageBoxW(0, errormsg
, iter
->title
, MB_YESNO
|
405 MB_ICONQUESTION
) == IDYES
)
407 /* delete the application's uninstall entry */
408 RegOpenKeyExW(iter
->regroot
, PathUninstallW
, 0, KEY_READ
, &hkey
);
409 RegDeleteKeyW(hkey
, iter
->regkey
);
419 /******************************************************************************
420 * Name : SupportInfoDlgProc
421 * Description: Callback procedure for support info dialog
422 * Parameters : hWnd - hWnd of the window
423 * msg - reason for calling function
424 * wParam - additional parameter
425 * lParam - additional parameter
426 * Returns : Dependant on message
428 static BOOL CALLBACK
SupportInfoDlgProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
431 WCHAR oldtitle
[MAX_STRING_LEN
];
432 WCHAR buf
[MAX_STRING_LEN
];
437 for (iter
= AppInfo
; iter
; iter
= iter
->next
)
439 if (iter
->id
== (int) lParam
)
441 /* Update the main label with the app name */
442 if (GetWindowTextW(GetDlgItem(hWnd
, IDC_INFO_LABEL
), oldtitle
,
443 MAX_STRING_LEN
) != 0)
445 wsprintfW(buf
, oldtitle
, iter
->title
);
446 SetWindowTextW(GetDlgItem(hWnd
, IDC_INFO_LABEL
), buf
);
459 switch (LOWORD(wParam
))
462 EndDialog(hWnd
, TRUE
);
473 /******************************************************************************
475 * Description: Displays the Support Information dialog
476 * Parameters : hWnd - Handle of the main dialog
477 * id - ID of the application to display information for
479 static void SupportInfo(HWND hWnd
, int id
)
481 DialogBoxParamW(hInst
, MAKEINTRESOURCEW(IDD_INFO
), hWnd
, (DLGPROC
)
482 SupportInfoDlgProc
, (LPARAM
) id
);
485 /* Definition of column headers for AddListViewColumns function */
486 typedef struct AppWizColumn
{
492 AppWizColumn columns
[] = {
493 {200, LVCFMT_LEFT
, IDS_COLUMN_NAME
},
494 {150, LVCFMT_LEFT
, IDS_COLUMN_PUBLISHER
},
495 {100, LVCFMT_LEFT
, IDS_COLUMN_VERSION
},
498 /******************************************************************************
499 * Name : AddListViewColumns
500 * Description: Adds column headers to the list view control.
501 * Parameters : hWnd - Handle of the list view control.
502 * Returns : TRUE if completed successfully, FALSE otherwise.
504 static BOOL
AddListViewColumns(HWND hWnd
)
506 WCHAR buf
[MAX_STRING_LEN
];
510 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_SUBITEM
| LVCF_WIDTH
;
512 /* Add the columns */
513 for (i
= 0; i
< sizeof(columns
) / sizeof(columns
[0]); i
++)
518 /* set width and format */
519 lvc
.cx
= columns
[i
].width
;
520 lvc
.fmt
= columns
[i
].fmt
;
522 LoadStringW(hInst
, columns
[i
].title
, buf
, sizeof(buf
) / sizeof(buf
[0]));
524 if (ListView_InsertColumnW(hWnd
, i
, &lvc
) == -1)
531 /******************************************************************************
532 * Name : AddListViewImageList
533 * Description: Creates an ImageList for the list view control.
534 * Parameters : hWnd - Handle of the list view control.
535 * Returns : Handle of the image list.
537 static HIMAGELIST
AddListViewImageList(HWND hWnd
)
542 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
545 /* Add default icon to image list */
546 hDefaultIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(ICO_MAIN
));
547 ImageList_AddIcon(hSmall
, hDefaultIcon
);
548 DestroyIcon(hDefaultIcon
);
550 (void) ListView_SetImageList(hWnd
, hSmall
, LVSIL_SMALL
);
555 /******************************************************************************
556 * Name : ResetApplicationList
557 * Description: Empties the app list, if need be, and recreates it.
558 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
559 * hWnd - handle of the dialog box
560 * hImageList - handle of the image list
561 * Returns : New handle of the image list.
563 static HIMAGELIST
ResetApplicationList(BOOL bFirstRun
, HWND hWnd
, HIMAGELIST hImageList
)
567 hWndListView
= GetDlgItem(hWnd
, IDL_PROGRAMS
);
569 /* if first run, create the image list and add the listview columns */
572 if (!AddListViewColumns(hWndListView
))
575 else /* we need to remove the existing things first */
577 RemoveItemsFromList(hWnd
);
578 ImageList_Destroy(hImageList
);
580 /* reset the list, since it's probably changed if the uninstallation was
585 /* now create the image list and add the applications to the listview */
586 hImageList
= AddListViewImageList(hWndListView
);
588 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE
);
589 ReadApplicationsFromRegistry(HKEY_CURRENT_USER
);
591 AddApplicationsToList(hWndListView
, hImageList
);
597 /******************************************************************************
599 * Description: Callback procedure for main tab
600 * Parameters : hWnd - hWnd of the window
601 * msg - reason for calling function
602 * wParam - additional parameter
603 * lParam - additional parameter
604 * Returns : Dependant on message
606 static BOOL CALLBACK
MainDlgProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
609 static HIMAGELIST hImageList
;
616 hImageList
= ResetApplicationList(TRUE
, hWnd
, hImageList
);
624 RemoveItemsFromList(hWnd
);
625 ImageList_Destroy(hImageList
);
632 nmh
= (LPNMHDR
) lParam
;
639 case LVN_ITEMCHANGED
:
649 switch (LOWORD(wParam
))
652 selitem
= SendDlgItemMessageW(hWnd
, IDL_PROGRAMS
,
653 LVM_GETNEXTITEM
, -1, LVNI_FOCUSED
|LVNI_SELECTED
);
657 lvItem
.iItem
= selitem
;
658 lvItem
.mask
= LVIF_PARAM
;
660 if (SendDlgItemMessageW(hWnd
, IDL_PROGRAMS
, LVM_GETITEMW
,
661 0, (LPARAM
) &lvItem
))
662 UninstallProgram(lvItem
.lParam
);
665 hImageList
= ResetApplicationList(FALSE
, hWnd
, hImageList
);
669 case IDC_SUPPORT_INFO
:
670 selitem
= SendDlgItemMessageW(hWnd
, IDL_PROGRAMS
,
671 LVM_GETNEXTITEM
, -1, LVNI_FOCUSED
| LVNI_SELECTED
);
675 lvItem
.iItem
= selitem
;
676 lvItem
.mask
= LVIF_PARAM
;
678 if (SendDlgItemMessageW(hWnd
, IDL_PROGRAMS
, LVM_GETITEMW
,
679 0, (LPARAM
) &lvItem
))
680 SupportInfo(hWnd
, lvItem
.lParam
);
692 /******************************************************************************
694 * Description: Main routine for applet
695 * Parameters : hWnd - hWnd of the Control Panel
697 static void StartApplet(HWND hWnd
)
700 PROPSHEETHEADERW psh
;
701 WCHAR tab_title
[MAX_STRING_LEN
], app_title
[MAX_STRING_LEN
];
703 /* Load the strings we will use */
704 LoadStringW(hInst
, IDS_TAB1_TITLE
, tab_title
, sizeof(tab_title
) / sizeof(tab_title
[0]));
705 LoadStringW(hInst
, IDS_CPL_TITLE
, app_title
, sizeof(app_title
) / sizeof(app_title
[0]));
707 /* Fill out the PROPSHEETPAGE */
708 psp
.dwSize
= sizeof (PROPSHEETPAGEW
);
709 psp
.dwFlags
= PSP_USETITLE
;
710 psp
.hInstance
= hInst
;
711 psp
.pszTemplate
= MAKEINTRESOURCEW (IDD_MAIN
);
713 psp
.pfnDlgProc
= (DLGPROC
) MainDlgProc
;
714 psp
.pszTitle
= tab_title
;
717 /* Fill out the PROPSHEETHEADER */
718 psh
.dwSize
= sizeof (PROPSHEETHEADERW
);
719 psh
.dwFlags
= PSH_PROPSHEETPAGE
| PSH_USEICONID
;
720 psh
.hwndParent
= hWnd
;
721 psh
.hInstance
= hInst
;
723 psh
.pszCaption
= app_title
;
726 psh
.pfnCallback
= NULL
;
729 /* Display the property sheet */
730 PropertySheetW (&psh
);
733 /******************************************************************************
735 * Description: Entry point for Control Panel applets
736 * Parameters : hwndCPL - hWnd of the Control Panel
737 * message - reason for calling function
738 * lParam1 - additional parameter
739 * lParam2 - additional parameter
740 * Returns : Dependant on message
742 LONG CALLBACK
CPlApplet(HWND hwndCPL
, UINT message
, LPARAM lParam1
, LPARAM lParam2
)
744 INITCOMMONCONTROLSEX iccEx
;
749 iccEx
.dwSize
= sizeof(iccEx
);
750 iccEx
.dwICC
= ICC_LISTVIEW_CLASSES
| ICC_TAB_CLASSES
;
752 InitCommonControlsEx(&iccEx
);
761 CPLINFO
*appletInfo
= (CPLINFO
*) lParam2
;
763 appletInfo
->idIcon
= ICO_MAIN
;
764 appletInfo
->idName
= IDS_CPL_TITLE
;
765 appletInfo
->idInfo
= IDS_CPL_DESC
;
766 appletInfo
->lData
= 0;
772 StartApplet(hwndCPL
);