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"
49 WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl);
51 /* define a maximum length for various buffers we use */
52 #define MAX_STRING_LEN 1024
54 typedef struct APPINFO {
67 WCHAR regkey[MAX_STRING_LEN];
72 static struct APPINFO *AppInfo = NULL;
73 static HINSTANCE hInst;
75 static const WCHAR openW[] = {'o','p','e','n',0};
77 /* names of registry keys */
78 static const WCHAR BackSlashW[] = { '\\', 0 };
79 static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
80 static const WCHAR DisplayIconW[] = {'D','i','s','p','l','a','y','I','c','o','n',0};
81 static const WCHAR DisplayVersionW[] = {'D','i','s','p','l','a','y','V','e','r',
83 static const WCHAR PublisherW[] = {'P','u','b','l','i','s','h','e','r',0};
84 static const WCHAR ContactW[] = {'C','o','n','t','a','c','t',0};
85 static const WCHAR HelpLinkW[] = {'H','e','l','p','L','i','n','k',0};
86 static const WCHAR HelpTelephoneW[] = {'H','e','l','p','T','e','l','e','p','h',
88 static const WCHAR ReadmeW[] = {'R','e','a','d','m','e',0};
89 static const WCHAR URLUpdateInfoW[] = {'U','R','L','U','p','d','a','t','e','I',
91 static const WCHAR CommentsW[] = {'C','o','m','m','e','n','t','s',0};
92 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
93 'S','t','r','i','n','g',0};
95 static const WCHAR PathUninstallW[] = {
96 'S','o','f','t','w','a','r','e','\\',
97 'M','i','c','r','o','s','o','f','t','\\',
98 'W','i','n','d','o','w','s','\\',
99 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
100 'U','n','i','n','s','t','a','l','l',0 };
102 /******************************************************************************
104 * Description: Entry point for DLL file
106 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
109 TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
113 case DLL_PROCESS_ATTACH:
120 /******************************************************************************
122 * Description: Frees memory used by an AppInfo structure, and any children.
124 static void FreeAppInfo(APPINFO *info)
128 APPINFO *next_info = info->next;
130 HeapFree(GetProcessHeap(), 0, info->title);
131 HeapFree(GetProcessHeap(), 0, info->path);
132 HeapFree(GetProcessHeap(), 0, info->icon);
133 HeapFree(GetProcessHeap(), 0, info->publisher);
134 HeapFree(GetProcessHeap(), 0, info->version);
135 HeapFree(GetProcessHeap(), 0, info);
140 /******************************************************************************
141 * Name : ReadApplicationsFromRegistry
142 * Description: Creates a linked list of uninstallable applications from the
144 * Parameters : root - Which registry root to read from (HKCU/HKLM)
145 * Returns : TRUE if successful, FALSE otherwise
147 static BOOL ReadApplicationsFromRegistry(HKEY root)
149 HKEY hkeyUninst, hkeyApp;
151 DWORD sizeOfSubKeyName, displen, uninstlen;
152 WCHAR subKeyName[256];
153 WCHAR key_app[MAX_STRING_LEN];
155 APPINFO *iter = AppInfo;
159 if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
163 lstrcpyW(key_app, PathUninstallW);
164 lstrcatW(key_app, BackSlashW);
165 p = key_app+lstrlenW(PathUninstallW)+1;
167 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
171 /* find the end of the list */
172 for (iter = AppInfo; iter->next; iter = iter->next);
175 for (i = 0; RegEnumKeyExW(hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL,
176 NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; ++i)
178 lstrcpyW(p, subKeyName);
179 RegOpenKeyExW(root, key_app, 0, KEY_READ, &hkeyApp);
184 if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) ==
185 ERROR_SUCCESS) && (RegQueryValueExW(hkeyApp, UninstallCommandlineW,
186 0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
188 /* if we already have iter, allocate the next entry */
191 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
192 sizeof(struct APPINFO));
201 /* if not, start the list */
202 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
203 sizeof(struct APPINFO));
209 iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
214 RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
217 /* now get DisplayIcon */
219 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
225 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
230 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
233 /* separate the index from the icon name, if supplied */
234 iconPtr = strchrW(iter->icon, ',');
239 iter->iconIdx = atoiW(iconPtr);
243 iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
248 RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
249 (LPBYTE)iter->path, &uninstlen);
251 /* publisher, version */
252 if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
255 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
257 if (!iter->publisher)
260 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
264 if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
267 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
272 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
277 iter->regroot = root;
278 lstrcpyW(iter->regkey, subKeyName);
283 RegCloseKey(hkeyApp);
284 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
291 RegCloseKey(hkeyApp);
295 RegCloseKey(hkeyUninst);
300 /******************************************************************************
301 * Name : AddApplicationsToList
302 * Description: Populates the list box with applications.
303 * Parameters : hWnd - Handle of the dialog box
305 static void AddApplicationsToList(HWND hWnd, HIMAGELIST hList)
307 APPINFO *iter = AppInfo;
319 if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
321 index = ImageList_AddIcon(hList, hIcon);
326 lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
327 lvItem.iItem = iter->id;
329 lvItem.pszText = iter->title;
330 lvItem.iImage = index;
331 lvItem.lParam = iter->id;
333 index = ListView_InsertItemW(hWnd, &lvItem);
335 /* now add the subitems (columns) */
336 ListView_SetItemTextW(hWnd, index, 1, iter->publisher);
337 ListView_SetItemTextW(hWnd, index, 2, iter->version);
343 /******************************************************************************
344 * Name : RemoveItemsFromList
345 * Description: Clears the application list box.
346 * Parameters : hWnd - Handle of the dialog box
348 static void RemoveItemsFromList(HWND hWnd)
350 SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_DELETEALLITEMS, 0, 0);
353 /******************************************************************************
355 * Description: Frees memory used by the application linked list.
357 static inline void EmptyList(void)
359 FreeAppInfo(AppInfo);
363 /******************************************************************************
364 * Name : UpdateButtons
365 * Description: Enables/disables the Add/Remove button depending on current
366 * selection in list box.
367 * Parameters : hWnd - Handle of the dialog box
369 static void UpdateButtons(HWND hWnd)
371 BOOL sel = SendMessageW(GetDlgItem(hWnd, IDL_PROGRAMS), LVM_GETSELECTEDCOUNT, 0, 0) != 0;
373 EnableWindow(GetDlgItem(hWnd, IDC_ADDREMOVE), sel);
374 EnableWindow(GetDlgItem(hWnd, IDC_SUPPORT_INFO), sel);
377 /******************************************************************************
378 * Name : InstallProgram
379 * Description: Search for potential Installer and execute it.
380 * Parameters : hWnd - Handle of the dialog box
382 static void InstallProgram(HWND hWnd)
385 WCHAR titleW[MAX_STRING_LEN];
386 WCHAR FilterBufferW[MAX_STRING_LEN];
387 WCHAR FileNameBufferW[MAX_PATH];
389 LoadStringW(hInst, IDS_CPL_TITLE, titleW, sizeof(titleW)/sizeof(WCHAR));
390 LoadStringW(hInst, IDS_INSTALL_FILTER, FilterBufferW, sizeof(FilterBufferW)/sizeof(WCHAR));
392 memset(&ofn, 0, sizeof(OPENFILENAMEW));
393 ofn.lStructSize = sizeof(OPENFILENAMEW);
394 ofn.hwndOwner = hWnd;
395 ofn.hInstance = hInst;
396 ofn.lpstrFilter = FilterBufferW;
397 ofn.nFilterIndex = 0;
398 ofn.lpstrFile = FileNameBufferW;
399 ofn.nMaxFile = MAX_PATH;
400 ofn.lpstrFileTitle = NULL;
401 ofn.nMaxFileTitle = 0;
402 ofn.lpstrTitle = titleW;
403 ofn.Flags = OFN_HIDEREADONLY | OFN_ENABLESIZING;
404 FileNameBufferW[0] = 0;
406 if (GetOpenFileNameW(&ofn))
408 SHELLEXECUTEINFOW sei;
409 memset(&sei, 0, sizeof(sei));
410 sei.cbSize = sizeof(sei);
412 sei.nShow = SW_SHOWDEFAULT;
413 sei.fMask = SEE_MASK_NO_CONSOLE;
414 sei.lpFile = ofn.lpstrFile;
416 ShellExecuteExW(&sei);
420 /******************************************************************************
421 * Name : UninstallProgram
422 * Description: Executes the specified program's installer.
423 * Parameters : id - the internal ID of the installer to remove
425 static void UninstallProgram(int id)
429 PROCESS_INFORMATION info;
430 WCHAR errormsg[MAX_STRING_LEN];
431 WCHAR sUninstallFailed[MAX_STRING_LEN];
435 LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
436 sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
438 for (iter = AppInfo; iter; iter = iter->next)
442 TRACE("Uninstalling %s (%s)\n", wine_dbgstr_w(iter->title),
443 wine_dbgstr_w(iter->path));
445 memset(&si, 0, sizeof(STARTUPINFOW));
446 si.cb = sizeof(STARTUPINFOW);
447 si.wShowWindow = SW_NORMAL;
448 res = CreateProcessW(NULL, iter->path, NULL, NULL, FALSE, 0, NULL,
453 CloseHandle(info.hThread);
455 /* wait for the process to exit */
456 WaitForSingleObject(info.hProcess, INFINITE);
457 CloseHandle(info.hProcess);
461 wsprintfW(errormsg, sUninstallFailed, iter->path);
463 if (MessageBoxW(0, errormsg, iter->title, MB_YESNO |
464 MB_ICONQUESTION) == IDYES)
466 /* delete the application's uninstall entry */
467 RegOpenKeyExW(iter->regroot, PathUninstallW, 0, KEY_READ, &hkey);
468 RegDeleteKeyW(hkey, iter->regkey);
478 /**********************************************************************************
479 * Name : SetInfoDialogText
480 * Description: Sets the text of a label in a window, based upon a registry entry
481 * or string passed to the function.
482 * Parameters : hKey - registry entry to read from, NULL if not reading
484 * lpKeyName - key to read from, or string to check if hKey is NULL
485 * lpAltMessage - alternative message if entry not found
486 * hWnd - handle of dialog box
487 * iDlgItem - ID of label in dialog box
489 static void SetInfoDialogText(HKEY hKey, LPWSTR lpKeyName, LPWSTR lpAltMessage,
490 HWND hWnd, int iDlgItem)
492 WCHAR buf[MAX_STRING_LEN];
496 hWndDlgItem = GetDlgItem(hWnd, iDlgItem);
498 /* if hKey is null, lpKeyName contains the string we want to check */
501 if ((lpKeyName) && (lstrlenW(lpKeyName) > 0))
502 SetWindowTextW(hWndDlgItem, lpKeyName);
504 SetWindowTextW(hWndDlgItem, lpAltMessage);
508 buflen = MAX_STRING_LEN;
510 if ((RegQueryValueExW(hKey, lpKeyName, 0, 0, (LPBYTE) buf, &buflen) ==
511 ERROR_SUCCESS) && (lstrlenW(buf) > 0))
512 SetWindowTextW(hWndDlgItem, buf);
514 SetWindowTextW(hWndDlgItem, lpAltMessage);
518 /******************************************************************************
519 * Name : SupportInfoDlgProc
520 * Description: Callback procedure for support info dialog
521 * Parameters : hWnd - hWnd of the window
522 * msg - reason for calling function
523 * wParam - additional parameter
524 * lParam - additional parameter
525 * Returns : Dependant on message
527 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
531 WCHAR oldtitle[MAX_STRING_LEN];
532 WCHAR buf[MAX_STRING_LEN];
533 WCHAR key[MAX_STRING_LEN];
534 WCHAR notfound[MAX_STRING_LEN];
539 for (iter = AppInfo; iter; iter = iter->next)
541 if (iter->id == (int) lParam)
543 lstrcpyW(key, PathUninstallW);
544 lstrcatW(key, BackSlashW);
545 lstrcatW(key, iter->regkey);
547 /* check the application's registry entries */
548 RegOpenKeyExW(iter->regroot, key, 0, KEY_READ, &hkey);
550 /* Load our "not specified" string */
551 LoadStringW(hInst, IDS_NOT_SPECIFIED, notfound,
552 sizeof(notfound) / sizeof(notfound[0]));
554 /* Update the data for items already read into the structure */
555 SetInfoDialogText(NULL, iter->publisher, notfound, hWnd,
557 SetInfoDialogText(NULL, iter->version, notfound, hWnd,
560 /* And now update the data for those items in the registry */
561 SetInfoDialogText(hkey, (LPWSTR) ContactW, notfound, hWnd,
563 SetInfoDialogText(hkey, (LPWSTR) HelpLinkW, notfound, hWnd,
565 SetInfoDialogText(hkey, (LPWSTR) HelpTelephoneW, notfound, hWnd,
567 SetInfoDialogText(hkey, (LPWSTR) ReadmeW, notfound, hWnd,
569 SetInfoDialogText(hkey, (LPWSTR) URLUpdateInfoW, notfound, hWnd,
571 SetInfoDialogText(hkey, (LPWSTR) CommentsW, notfound, hWnd,
574 /* Update the main label with the app name */
575 if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
576 MAX_STRING_LEN) != 0)
578 wsprintfW(buf, oldtitle, iter->title);
579 SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
594 switch (LOWORD(wParam))
597 EndDialog(hWnd, TRUE);
608 /******************************************************************************
610 * Description: Displays the Support Information dialog
611 * Parameters : hWnd - Handle of the main dialog
612 * id - ID of the application to display information for
614 static void SupportInfo(HWND hWnd, int id)
616 DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
617 SupportInfoDlgProc, (LPARAM) id);
620 /* Definition of column headers for AddListViewColumns function */
621 typedef struct AppWizColumn {
627 static const AppWizColumn columns[] = {
628 {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
629 {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
630 {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
633 /******************************************************************************
634 * Name : AddListViewColumns
635 * Description: Adds column headers to the list view control.
636 * Parameters : hWnd - Handle of the list view control.
637 * Returns : TRUE if completed successfully, FALSE otherwise.
639 static BOOL AddListViewColumns(HWND hWnd)
641 WCHAR buf[MAX_STRING_LEN];
645 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
647 /* Add the columns */
648 for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
653 /* set width and format */
654 lvc.cx = columns[i].width;
655 lvc.fmt = columns[i].fmt;
657 LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
659 if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
666 /******************************************************************************
667 * Name : AddListViewImageList
668 * Description: Creates an ImageList for the list view control.
669 * Parameters : hWnd - Handle of the list view control.
670 * Returns : Handle of the image list.
672 static HIMAGELIST AddListViewImageList(HWND hWnd)
677 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
680 /* Add default icon to image list */
681 hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
682 ImageList_AddIcon(hSmall, hDefaultIcon);
683 DestroyIcon(hDefaultIcon);
685 SendMessageW(hWnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)hSmall);
690 /******************************************************************************
691 * Name : ResetApplicationList
692 * Description: Empties the app list, if need be, and recreates it.
693 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
694 * hWnd - handle of the dialog box
695 * hImageList - handle of the image list
696 * Returns : New handle of the image list.
698 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
702 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
704 /* if first run, create the image list and add the listview columns */
707 if (!AddListViewColumns(hWndListView))
710 else /* we need to remove the existing things first */
712 RemoveItemsFromList(hWnd);
713 ImageList_Destroy(hImageList);
715 /* reset the list, since it's probably changed if the uninstallation was
720 /* now create the image list and add the applications to the listview */
721 hImageList = AddListViewImageList(hWndListView);
723 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
724 ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
726 AddApplicationsToList(hWndListView, hImageList);
732 /******************************************************************************
734 * Description: Callback procedure for main tab
735 * Parameters : hWnd - hWnd of the window
736 * msg - reason for calling function
737 * wParam - additional parameter
738 * lParam - additional parameter
739 * Returns : Dependant on message
741 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
744 static HIMAGELIST hImageList;
751 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
759 RemoveItemsFromList(hWnd);
760 ImageList_Destroy(hImageList);
767 nmh = (LPNMHDR) lParam;
774 case LVN_ITEMCHANGED:
784 switch (LOWORD(wParam))
787 InstallProgram(hWnd);
791 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
792 LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
796 lvItem.iItem = selitem;
797 lvItem.mask = LVIF_PARAM;
799 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
800 0, (LPARAM) &lvItem))
801 UninstallProgram(lvItem.lParam);
804 hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
808 case IDC_SUPPORT_INFO:
809 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
810 LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
814 lvItem.iItem = selitem;
815 lvItem.mask = LVIF_PARAM;
817 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
818 0, (LPARAM) &lvItem))
819 SupportInfo(hWnd, lvItem.lParam);
831 /******************************************************************************
833 * Description: Main routine for applet
834 * Parameters : hWnd - hWnd of the Control Panel
836 static void StartApplet(HWND hWnd)
839 PROPSHEETHEADERW psh;
840 WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
842 /* Load the strings we will use */
843 LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
844 LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
846 /* Fill out the PROPSHEETPAGE */
847 psp.dwSize = sizeof (PROPSHEETPAGEW);
848 psp.dwFlags = PSP_USETITLE;
849 psp.hInstance = hInst;
850 psp.u.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
851 psp.u2.pszIcon = NULL;
852 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
853 psp.pszTitle = tab_title;
856 /* Fill out the PROPSHEETHEADER */
857 psh.dwSize = sizeof (PROPSHEETHEADERW);
858 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
859 psh.hwndParent = hWnd;
860 psh.hInstance = hInst;
861 psh.u.pszIcon = NULL;
862 psh.pszCaption = app_title;
865 psh.pfnCallback = NULL;
866 psh.u2.nStartPage = 0;
868 /* Display the property sheet */
869 PropertySheetW (&psh);
872 /******************************************************************************
874 * Description: Entry point for Control Panel applets
875 * Parameters : hwndCPL - hWnd of the Control Panel
876 * message - reason for calling function
877 * lParam1 - additional parameter
878 * lParam2 - additional parameter
879 * Returns : Dependant on message
881 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
883 INITCOMMONCONTROLSEX iccEx;
888 iccEx.dwSize = sizeof(iccEx);
889 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
891 InitCommonControlsEx(&iccEx);
900 CPLINFO *appletInfo = (CPLINFO *) lParam2;
902 appletInfo->idIcon = ICO_MAIN;
903 appletInfo->idName = IDS_CPL_TITLE;
904 appletInfo->idInfo = IDS_CPL_DESC;
905 appletInfo->lData = 0;
911 StartApplet(hwndCPL);