appwiz.cpl: Do not access memory after HeapFree in FreeAppInfo.
[wine/gsoc_dplay.git] / dlls / appwiz.cpl / appwiz.c
blobe06d8100087ffb18b1c16993cbc2f18869662c2e
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 while (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);
123 info = next_info;
127 /******************************************************************************
128 * Name : ReadApplicationsFromRegistry
129 * Description: Creates a linked list of uninstallable applications from the
130 * registry.
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;
137 int i, id = 0;
138 DWORD sizeOfSubKeyName, displen, uninstlen;
139 WCHAR subKeyName[256];
140 WCHAR key_app[MAX_STRING_LEN];
141 WCHAR *p;
142 APPINFO *iter = AppInfo;
143 LPWSTR iconPtr;
144 BOOL ret = FALSE;
146 if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
147 ERROR_SUCCESS)
148 return FALSE;
150 lstrcpyW(key_app, PathUninstallW);
151 lstrcatW(key_app, BackSlashW);
152 p = key_app+lstrlenW(PathUninstallW)+1;
154 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
156 if (iter)
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);
168 displen = 0;
169 uninstlen = 0;
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 */
176 if (iter)
178 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
179 sizeof(struct APPINFO));
181 if (!iter->next)
182 goto err;
184 iter = iter->next;
186 else
188 /* if not, start the list */
189 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
190 sizeof(struct APPINFO));
192 if (!iter)
193 goto err;
196 iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
198 if (!iter->title)
199 goto err;
201 RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
202 &displen);
204 /* now get DisplayIcon */
205 displen = 0;
206 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
208 if (displen == 0)
209 iter->icon = 0;
210 else
212 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
214 if (!iter->icon)
215 goto err;
217 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
218 &displen);
220 /* separate the index from the icon name, if supplied */
221 iconPtr = strchrW(iter->icon, ',');
223 if (iconPtr)
225 *iconPtr++ = 0;
226 iter->iconIdx = atoiW(iconPtr);
230 iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
232 if (!iter->path)
233 goto err;
235 RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
236 (LPBYTE)iter->path, &uninstlen);
238 /* publisher, version */
239 if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
240 ERROR_SUCCESS)
242 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
244 if (!iter->publisher)
245 goto err;
247 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
248 &displen);
251 if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
252 ERROR_SUCCESS)
254 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
256 if (!iter->version)
257 goto err;
259 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
260 &displen);
263 /* registry key */
264 iter->regroot = root;
265 lstrcpyW(iter->regkey, subKeyName);
267 iter->id = id++;
270 RegCloseKey(hkeyApp);
271 sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
274 ret = TRUE;
275 goto end;
277 err:
278 RegCloseKey(hkeyApp);
279 FreeAppInfo(iter);
281 end:
282 RegCloseKey(hkeyUninst);
283 return ret;
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;
295 LVITEMW lvItem;
296 HICON hIcon;
297 int index;
299 while (iter)
301 /* get the icon */
302 index = 0;
304 if (iter->icon)
306 if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
308 index = ImageList_AddIcon(hList, hIcon);
309 DestroyIcon(hIcon);
313 lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
314 lvItem.iItem = iter->id;
315 lvItem.iSubItem = 0;
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);
326 iter = iter->next;
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 /******************************************************************************
341 * Name : EmptyList
342 * Description: Frees memory used by the application linked list.
344 static inline void EmptyList(void)
346 FreeAppInfo(AppInfo);
347 AppInfo = NULL;
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)
371 APPINFO *iter;
372 STARTUPINFOW si;
373 PROCESS_INFORMATION info;
374 WCHAR errormsg[MAX_STRING_LEN];
375 WCHAR sUninstallFailed[MAX_STRING_LEN];
376 HKEY hkey;
377 BOOL res;
379 LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
380 sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
382 for (iter = AppInfo; iter; iter = iter->next)
384 if (iter->id == id)
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,
393 NULL, &si, &info);
395 if (res)
397 /* wait for the process to exit */
398 WaitForSingleObject(info.hProcess, INFINITE);
400 else
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);
410 RegCloseKey(hkey);
414 break;
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)
430 APPINFO *iter;
431 WCHAR oldtitle[MAX_STRING_LEN];
432 WCHAR buf[MAX_STRING_LEN];
434 switch(msg)
436 case WM_INITDIALOG:
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);
449 break;
453 return TRUE;
455 case WM_DESTROY:
456 return 0;
458 case WM_COMMAND:
459 switch (LOWORD(wParam))
461 case IDOK:
462 EndDialog(hWnd, TRUE);
463 break;
467 return TRUE;
470 return FALSE;
473 /******************************************************************************
474 * Name : SupportInfo
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 {
487 int width;
488 int fmt;
489 int title;
490 } 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];
507 LVCOLUMNW lvc;
508 int i;
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++)
515 lvc.iSubItem = i;
516 lvc.pszText = buf;
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)
525 return FALSE;
528 return TRUE;
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)
539 HIMAGELIST hSmall;
540 HICON hDefaultIcon;
542 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
543 ILC_MASK, 1, 1);
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);
552 return hSmall;
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)
565 HWND hWndListView;
567 hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
569 /* if first run, create the image list and add the listview columns */
570 if (bFirstRun)
572 if (!AddListViewColumns(hWndListView))
573 return NULL;
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
581 successful */
582 EmptyList();
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);
592 UpdateButtons(hWnd);
594 return(hImageList);
597 /******************************************************************************
598 * Name : MainDlgProc
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)
608 int selitem;
609 static HIMAGELIST hImageList;
610 LPNMHDR nmh;
611 LVITEMW lvItem;
613 switch(msg)
615 case WM_INITDIALOG:
616 hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
618 if (!hImageList)
619 return FALSE;
621 return TRUE;
623 case WM_DESTROY:
624 RemoveItemsFromList(hWnd);
625 ImageList_Destroy(hImageList);
627 EmptyList();
629 return 0;
631 case WM_NOTIFY:
632 nmh = (LPNMHDR) lParam;
634 switch (nmh->idFrom)
636 case IDL_PROGRAMS:
637 switch (nmh->code)
639 case LVN_ITEMCHANGED:
640 UpdateButtons(hWnd);
641 break;
643 break;
646 return TRUE;
648 case WM_COMMAND:
649 switch (LOWORD(wParam))
651 case IDC_ADDREMOVE:
652 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
653 LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
655 if (selitem != -1)
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);
667 break;
669 case IDC_SUPPORT_INFO:
670 selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
671 LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
673 if (selitem != -1)
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);
683 break;
686 return TRUE;
689 return FALSE;
692 /******************************************************************************
693 * Name : StartApplet
694 * Description: Main routine for applet
695 * Parameters : hWnd - hWnd of the Control Panel
697 static void StartApplet(HWND hWnd)
699 PROPSHEETPAGEW psp;
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);
712 psp.pszIcon = NULL;
713 psp.pfnDlgProc = (DLGPROC) MainDlgProc;
714 psp.pszTitle = tab_title;
715 psp.lParam = 0;
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;
722 psh.pszIcon = NULL;
723 psh.pszCaption = app_title;
724 psh.nPages = 1;
725 psh.ppsp = &psp;
726 psh.pfnCallback = NULL;
727 psh.nStartPage = 0;
729 /* Display the property sheet */
730 PropertySheetW (&psh);
733 /******************************************************************************
734 * Name : CPlApplet
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;
746 switch (message)
748 case CPL_INIT:
749 iccEx.dwSize = sizeof(iccEx);
750 iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
752 InitCommonControlsEx(&iccEx);
754 return TRUE;
756 case CPL_GETCOUNT:
757 return 1;
759 case CPL_INQUIRE:
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;
768 break;
771 case CPL_DBLCLK:
772 StartApplet(hwndCPL);
773 break;
776 return FALSE;