push decde5eed3d79f9d889b4d757f73e86ce6ff9241
[wine/hacks.git] / dlls / appwiz.cpl / appwiz.c
bloba13dea1d7955f774662e86660c64ecfdda1ff3e5
1 /*
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 #include "config.h"
27 #include "wine/port.h"
28 #include "wine/unicode.h"
29 #include "wine/debug.h"
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <windef.h>
36 #include <winbase.h>
37 #include <winuser.h>
38 #include <wingdi.h>
39 #include <winreg.h>
40 #include <shellapi.h>
41 #include <commctrl.h>
42 #include <cpl.h>
44 #include "res.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 {
52 int id;
54 LPWSTR title;
55 LPWSTR path;
57 LPWSTR icon;
58 int iconIdx;
60 LPWSTR publisher;
61 LPWSTR version;
63 HKEY regroot;
64 WCHAR regkey[MAX_STRING_LEN];
66 struct APPINFO *next;
67 } APPINFO;
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',
77 's','i','o','n',0};
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 /******************************************************************************
90 * Name : DllMain
91 * Description: Entry point for DLL file
93 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
94 LPVOID lpvReserved)
96 TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
98 switch (fdwReason)
100 case DLL_PROCESS_ATTACH:
101 hInst = hinstDLL;
102 break;
104 return TRUE;
107 /******************************************************************************
108 * Name : FreeAppInfo
109 * Description: Frees memory used by an AppInfo structure, and any children.
111 static void FreeAppInfo(APPINFO *info)
113 APPINFO *iter = info;
115 while (iter)
117 if (iter->title)
118 HeapFree(GetProcessHeap(), 0, iter->title);
120 if (iter->path)
121 HeapFree(GetProcessHeap(), 0, iter->path);
123 if (iter->icon)
124 HeapFree(GetProcessHeap(), 0, iter->icon);
126 if (iter->publisher)
127 HeapFree(GetProcessHeap(), 0, iter->publisher);
129 if (iter->version)
130 HeapFree(GetProcessHeap(), 0, iter->version);
132 iter = iter->next;
133 HeapFree(GetProcessHeap(), 0, iter);
137 /******************************************************************************
138 * Name : ReadApplicationsFromRegistry
139 * Description: Creates a linked list of uninstallable applications from the
140 * registry.
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;
147 int i, id = 0;
148 DWORD sizeOfSubKeyName, displen, uninstlen;
149 WCHAR subKeyName[256];
150 WCHAR key_app[MAX_STRING_LEN];
151 WCHAR *p;
152 APPINFO *iter = AppInfo;
153 LPWSTR iconPtr;
154 BOOL ret = FALSE;
156 if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
157 ERROR_SUCCESS)
158 return FALSE;
160 lstrcpyW(key_app, PathUninstallW);
161 lstrcatW(key_app, BackSlashW);
162 p = key_app+lstrlenW(PathUninstallW)+1;
164 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
166 if (iter)
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);
178 displen = 0;
179 uninstlen = 0;
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 */
186 if (iter)
188 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
189 sizeof(struct APPINFO));
191 if (!iter->next)
192 goto err;
194 iter = iter->next;
196 else
198 /* if not, start the list */
199 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
200 sizeof(struct APPINFO));
202 if (!iter)
203 goto err;
206 iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
208 if (!iter->title)
209 goto err;
211 RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
212 &displen);
214 /* now get DisplayIcon */
215 displen = 0;
216 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
218 if (displen == 0)
219 iter->icon = 0;
220 else
222 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
224 if (!iter->icon)
225 goto err;
227 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
228 &displen);
230 /* separate the index from the icon name, if supplied */
231 iconPtr = strchrW(iter->icon, ',');
233 if (iconPtr)
235 *iconPtr++ = 0;
236 iter->iconIdx = atoiW(iconPtr);
240 iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
242 if (!iter->path)
243 goto err;
245 RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
246 (LPBYTE)iter->path, &uninstlen);
248 /* publisher, version */
249 if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
250 ERROR_SUCCESS)
252 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
254 if (!iter->publisher)
255 goto err;
257 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
258 &displen);
261 if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
262 ERROR_SUCCESS)
264 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
266 if (!iter->version)
267 goto err;
269 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
270 &displen);
273 /* registry key */
274 iter->regroot = root;
275 lstrcpyW(iter->regkey, subKeyName);
277 iter->id = id++;
280 RegCloseKey(hkeyApp);
281 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
284 ret = TRUE;
285 goto end;
287 err:
288 RegCloseKey(hkeyApp);
289 FreeAppInfo(iter);
291 end:
292 RegCloseKey(hkeyUninst);
293 return ret;
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;
305 LVITEMW lvItem;
306 HICON hIcon;
307 int index;
309 while (iter)
311 /* get the icon */
312 index = 0;
314 if (iter->icon)
316 if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
318 index = ImageList_AddIcon(hList, hIcon);
319 DestroyIcon(hIcon);
323 lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
324 lvItem.iItem = iter->id;
325 lvItem.iSubItem = 0;
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);
336 iter = iter->next;
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 /******************************************************************************
351 * Name : EmptyList
352 * Description: Frees memory used by the application linked list.
354 static inline void EmptyList(void)
356 FreeAppInfo(AppInfo);
357 AppInfo = NULL;
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)
381 APPINFO *iter;
382 STARTUPINFOW si;
383 PROCESS_INFORMATION info;
384 WCHAR errormsg[MAX_STRING_LEN];
385 WCHAR sUninstallFailed[MAX_STRING_LEN];
386 HKEY hkey;
387 BOOL res;
389 LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
390 sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
392 for (iter = AppInfo; iter; iter = iter->next)
394 if (iter->id == id)
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,
403 NULL, &si, &info);
405 if (res)
407 /* wait for the process to exit */
408 WaitForSingleObject(info.hProcess, INFINITE);
410 else
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);
420 RegCloseKey(hkey);
424 break;
429 /******************************************************************************
430 * Name : SupportInfoDlgProc
431 * Description: Callback procedure for support info dialog
432 * Parameters : hWnd - hWnd of the window
433 * msg - reason for calling function
434 * wParam - additional parameter
435 * lParam - additional parameter
436 * Returns : Dependant on message
438 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
440 APPINFO *iter;
441 WCHAR oldtitle[MAX_STRING_LEN];
442 WCHAR buf[MAX_STRING_LEN];
444 switch(msg)
446 case WM_INITDIALOG:
447 for (iter = AppInfo; iter; iter = iter->next)
449 if (iter->id == (int) lParam)
451 /* Update the main label with the app name */
452 if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
453 MAX_STRING_LEN) != 0)
455 wsprintfW(buf, oldtitle, iter->title);
456 SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
459 break;
463 return TRUE;
465 case WM_DESTROY:
466 return 0;
468 case WM_COMMAND:
469 switch (LOWORD(wParam))
471 case IDOK:
472 EndDialog(hWnd, TRUE);
473 break;
477 return TRUE;
480 return FALSE;
483 /******************************************************************************
484 * Name : SupportInfo
485 * Description: Displays the Support Information dialog
486 * Parameters : hWnd - Handle of the main dialog
487 * id - ID of the application to display information for
489 static void SupportInfo(HWND hWnd, int id)
491 DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
492 SupportInfoDlgProc, (LPARAM) id);
495 /* Definition of column headers for AddListViewColumns function */
496 typedef struct AppWizColumn {
497 int width;
498 int fmt;
499 int title;
500 } AppWizColumn;
502 AppWizColumn columns[] = {
503 {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
504 {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
505 {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
508 /******************************************************************************
509 * Name : AddListViewColumns
510 * Description: Adds column headers to the list view control.
511 * Parameters : hWnd - Handle of the list view control.
512 * Returns : TRUE if completed successfully, FALSE otherwise.
514 static BOOL AddListViewColumns(HWND hWnd)
516 WCHAR buf[MAX_STRING_LEN];
517 LVCOLUMNW lvc;
518 int i;
520 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
522 /* Add the columns */
523 for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
525 lvc.iSubItem = i;
526 lvc.pszText = buf;
528 /* set width and format */
529 lvc.cx = columns[i].width;
530 lvc.fmt = columns[i].fmt;
532 LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
534 if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
535 return FALSE;
538 return TRUE;
541 /******************************************************************************
542 * Name : AddListViewImageList
543 * Description: Creates an ImageList for the list view control.
544 * Parameters : hWnd - Handle of the list view control.
545 * Returns : Handle of the image list.
547 static HIMAGELIST AddListViewImageList(HWND hWnd)
549 HIMAGELIST hSmall;
550 HICON hDefaultIcon;
552 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
553 ILC_MASK, 1, 1);
555 /* Add default icon to image list */
556 hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
557 ImageList_AddIcon(hSmall, hDefaultIcon);
558 DestroyIcon(hDefaultIcon);
560 (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
562 return hSmall;
565 /******************************************************************************
566 * Name : ResetApplicationList
567 * Description: Empties the app list, if need be, and recreates it.
568 * Parameters : bFirstRun - TRUE if this is the first time this is run, FALSE otherwise
569 * hWnd - handle of the dialog box
570 * hImageList - handle of the image list
571 * Returns : New handle of the image list.
573 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
575 HWND hWndListView;
577 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
579 /* if first run, create the image list and add the listview columns */
580 if (bFirstRun)
582 if (!AddListViewColumns(hWndListView))
583 return NULL;
585 else /* we need to remove the existing things first */
587 RemoveItemsFromList(hWnd);
588 ImageList_Destroy(hImageList);
590 /* reset the list, since it's probably changed if the uninstallation was
591 successful */
592 EmptyList();
595 /* now create the image list and add the applications to the listview */
596 hImageList = AddListViewImageList(hWndListView);
598 ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
599 ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
601 AddApplicationsToList(hWndListView, hImageList);
602 UpdateButtons(hWnd);
604 return(hImageList);
607 /******************************************************************************
608 * Name : MainDlgProc
609 * Description: Callback procedure for main tab
610 * Parameters : hWnd - hWnd of the window
611 * msg - reason for calling function
612 * wParam - additional parameter
613 * lParam - additional parameter
614 * Returns : Dependant on message
616 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
618 int selitem;
619 static HIMAGELIST hImageList;
620 LPNMHDR nmh;
621 LVITEMW lvItem;
623 switch(msg)
625 case WM_INITDIALOG:
626 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
628 if (!hImageList)
629 return FALSE;
631 return TRUE;
633 case WM_DESTROY:
634 RemoveItemsFromList(hWnd);
635 ImageList_Destroy(hImageList);
637 EmptyList();
639 return 0;
641 case WM_NOTIFY:
642 nmh = (LPNMHDR) lParam;
644 switch (nmh->idFrom)
646 case IDL_PROGRAMS:
647 switch (nmh->code)
649 case LVN_ITEMCHANGED:
650 UpdateButtons(hWnd);
651 break;
653 break;
656 return TRUE;
658 case WM_COMMAND:
659 switch (LOWORD(wParam))
661 case IDC_ADDREMOVE:
662 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
663 LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
665 if (selitem != -1)
667 lvItem.iItem = selitem;
668 lvItem.mask = LVIF_PARAM;
670 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
671 0, (LPARAM) &lvItem))
672 UninstallProgram(lvItem.lParam);
675 hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
677 break;
679 case IDC_SUPPORT_INFO:
680 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
681 LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
683 if (selitem != -1)
685 lvItem.iItem = selitem;
686 lvItem.mask = LVIF_PARAM;
688 if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
689 0, (LPARAM) &lvItem))
690 SupportInfo(hWnd, lvItem.lParam);
693 break;
696 return TRUE;
699 return FALSE;
702 /******************************************************************************
703 * Name : StartApplet
704 * Description: Main routine for applet
705 * Parameters : hWnd - hWnd of the Control Panel
707 static void StartApplet(HWND hWnd)
709 PROPSHEETPAGEW psp;
710 PROPSHEETHEADERW psh;
711 WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
713 /* Load the strings we will use */
714 LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
715 LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
717 /* Fill out the PROPSHEETPAGE */
718 psp.dwSize = sizeof (PROPSHEETPAGEW);
719 psp.dwFlags = PSP_USETITLE;
720 psp.hInstance = hInst;
721 psp.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
722 psp.pszIcon = NULL;
723 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
724 psp.pszTitle = tab_title;
725 psp.lParam = 0;
727 /* Fill out the PROPSHEETHEADER */
728 psh.dwSize = sizeof (PROPSHEETHEADERW);
729 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
730 psh.hwndParent = hWnd;
731 psh.hInstance = hInst;
732 psh.pszIcon = NULL;
733 psh.pszCaption = app_title;
734 psh.nPages = 1;
735 psh.ppsp = &psp;
736 psh.pfnCallback = NULL;
737 psh.nStartPage = 0;
739 /* Display the property sheet */
740 PropertySheetW (&psh);
743 /******************************************************************************
744 * Name : CPlApplet
745 * Description: Entry point for Control Panel applets
746 * Parameters : hwndCPL - hWnd of the Control Panel
747 * message - reason for calling function
748 * lParam1 - additional parameter
749 * lParam2 - additional parameter
750 * Returns : Dependant on message
752 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
754 INITCOMMONCONTROLSEX iccEx;
756 switch (message)
758 case CPL_INIT:
759 iccEx.dwSize = sizeof(iccEx);
760 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
762 InitCommonControlsEx(&iccEx);
764 return TRUE;
766 case CPL_GETCOUNT:
767 return 1;
769 case CPL_INQUIRE:
771 CPLINFO *appletInfo = (CPLINFO *) lParam2;
773 appletInfo->idIcon = ICO_MAIN;
774 appletInfo->idName = IDS_CPL_TITLE;
775 appletInfo->idInfo = IDS_CPL_DESC;
776 appletInfo->lData = 0;
778 break;
781 case CPL_DBLCLK:
782 StartApplet(hwndCPL);
783 break;
786 return FALSE;