Add some API documentation stubs to make winapi_check happy.
[wine] / dlls / appwiz.cpl / appwiz.c
1 /*
2  * Add/Remove Programs applet
3  * Partially based on Wine Uninstaller
4  *
5  * Copyright 2000 Andreas Mohr
6  * Copyright 2004 Hannu Valtonen
7  * Copyright 2005 Jonathan Ernst
8  * Copyright 2001-2002, 2008 Owen Rudge
9  *
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.
14  *
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.
19  *
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
23  *
24  */
25
26 #define NONAMELESSUNION
27
28 #include "config.h"
29 #include "wine/port.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <windef.h>
38 #include <winbase.h>
39 #include <winuser.h>
40 #include <wingdi.h>
41 #include <winreg.h>
42 #include <shellapi.h>
43 #include <commctrl.h>
44 #include <cpl.h>
45
46 #include "res.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl);
49
50 /* define a maximum length for various buffers we use */
51 #define MAX_STRING_LEN    1024
52
53 typedef struct APPINFO {
54     int id;
55
56     LPWSTR title;
57     LPWSTR path;
58
59     LPWSTR icon;
60     int iconIdx;
61
62     LPWSTR publisher;
63     LPWSTR version;
64
65     HKEY regroot;
66     WCHAR regkey[MAX_STRING_LEN];
67
68     struct APPINFO *next;
69 } APPINFO;
70
71 static struct APPINFO *AppInfo = NULL;
72 static HINSTANCE hInst;
73
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',
79     's','i','o','n',0};
80 static const WCHAR PublisherW[] = {'P','u','b','l','i','s','h','e','r',0};
81 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
82     'S','t','r','i','n','g',0};
83
84 static const WCHAR PathUninstallW[] = {
85         'S','o','f','t','w','a','r','e','\\',
86         'M','i','c','r','o','s','o','f','t','\\',
87         'W','i','n','d','o','w','s','\\',
88         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
89         'U','n','i','n','s','t','a','l','l',0 };
90
91 /******************************************************************************
92  * Name       : DllMain
93  * Description: Entry point for DLL file
94  */
95 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
96                     LPVOID lpvReserved)
97 {
98     TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
99
100     switch (fdwReason)
101     {
102         case DLL_PROCESS_ATTACH:
103             hInst = hinstDLL;
104             break;
105     }
106     return TRUE;
107 }
108
109 /******************************************************************************
110  * Name       : FreeAppInfo
111  * Description: Frees memory used by an AppInfo structure, and any children.
112  */
113 static void FreeAppInfo(APPINFO *info)
114 {
115     while (info)
116     {
117         APPINFO *next_info = info->next;
118
119         HeapFree(GetProcessHeap(), 0, info->title);
120         HeapFree(GetProcessHeap(), 0, info->path);
121         HeapFree(GetProcessHeap(), 0, info->icon);
122         HeapFree(GetProcessHeap(), 0, info->publisher);
123         HeapFree(GetProcessHeap(), 0, info->version);
124         HeapFree(GetProcessHeap(), 0, info);
125         info = next_info;
126     }
127 }
128
129 /******************************************************************************
130  * Name       : ReadApplicationsFromRegistry
131  * Description: Creates a linked list of uninstallable applications from the
132  *              registry.
133  * Parameters : root    - Which registry root to read from (HKCU/HKLM)
134  * Returns    : TRUE if successful, FALSE otherwise
135  */
136 static BOOL ReadApplicationsFromRegistry(HKEY root)
137 {
138     HKEY hkeyUninst, hkeyApp;
139     int i, id = 0;
140     DWORD sizeOfSubKeyName, displen, uninstlen;
141     WCHAR subKeyName[256];
142     WCHAR key_app[MAX_STRING_LEN];
143     WCHAR *p;
144     APPINFO *iter = AppInfo;
145     LPWSTR iconPtr;
146     BOOL ret = FALSE;
147
148     if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
149       ERROR_SUCCESS)
150         return FALSE;
151
152     lstrcpyW(key_app, PathUninstallW);
153     lstrcatW(key_app, BackSlashW);
154     p = key_app+lstrlenW(PathUninstallW)+1;
155
156     sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
157
158     if (iter)
159     {
160         /* find the end of the list */
161         for (iter = AppInfo; iter->next; iter = iter->next);
162     }
163
164     for (i = 0; RegEnumKeyExW(hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL,
165         NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; ++i)
166     {
167         lstrcpyW(p, subKeyName);
168         RegOpenKeyExW(root, key_app, 0, KEY_READ, &hkeyApp);
169
170         displen = 0;
171         uninstlen = 0;
172
173         if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) ==
174             ERROR_SUCCESS) && (RegQueryValueExW(hkeyApp, UninstallCommandlineW,
175             0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
176         {
177             /* if we already have iter, allocate the next entry */
178             if (iter)
179             {
180                 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
181                     sizeof(struct APPINFO));
182
183                 if (!iter->next)
184                     goto err;
185
186                 iter = iter->next;
187             }
188             else
189             {
190                 /* if not, start the list */
191                 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
192                     sizeof(struct APPINFO));
193
194                 if (!iter)
195                     goto err;
196             }
197
198             iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
199
200             if (!iter->title)
201                 goto err;
202
203             RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
204                 &displen);
205
206             /* now get DisplayIcon */
207             displen = 0;
208             RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
209
210             if (displen == 0)
211                 iter->icon = 0;
212             else
213             {
214                 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
215
216                 if (!iter->icon)
217                     goto err;
218
219                 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
220                     &displen);
221
222                 /* separate the index from the icon name, if supplied */
223                 iconPtr = strchrW(iter->icon, ',');
224
225                 if (iconPtr)
226                 {
227                     *iconPtr++ = 0;
228                     iter->iconIdx = atoiW(iconPtr);
229                 }
230             }
231
232             iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
233
234             if (!iter->path)
235                 goto err;
236
237             RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
238                 (LPBYTE)iter->path, &uninstlen);
239
240             /* publisher, version */
241             if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
242                 ERROR_SUCCESS)
243             {
244                 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
245
246                 if (!iter->publisher)
247                     goto err;
248
249                 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
250                     &displen);
251             }
252
253             if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
254                 ERROR_SUCCESS)
255             {
256                 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
257
258                 if (!iter->version)
259                     goto err;
260
261                 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
262                     &displen);
263             }
264
265             /* registry key */
266             iter->regroot = root;
267             lstrcpyW(iter->regkey, subKeyName);
268
269             iter->id = id++;
270         }
271
272         RegCloseKey(hkeyApp);
273         sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
274     }
275
276     ret = TRUE;
277     goto end;
278
279 err:
280     RegCloseKey(hkeyApp);
281     FreeAppInfo(iter);
282
283 end:
284     RegCloseKey(hkeyUninst);
285     return ret;
286 }
287
288
289 /******************************************************************************
290  * Name       : AddApplicationsToList
291  * Description: Populates the list box with applications.
292  * Parameters : hWnd    - Handle of the dialog box
293  */
294 static void AddApplicationsToList(HWND hWnd, HIMAGELIST hList)
295 {
296     APPINFO *iter = AppInfo;
297     LVITEMW lvItem;
298     HICON hIcon;
299     int index;
300
301     while (iter)
302     {
303         /* get the icon */
304         index = 0;
305
306         if (iter->icon)
307         {
308             if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
309             {
310                 index = ImageList_AddIcon(hList, hIcon);
311                 DestroyIcon(hIcon);
312             }
313         }
314
315         lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
316         lvItem.iItem = iter->id;
317         lvItem.iSubItem = 0;
318         lvItem.pszText = iter->title;
319         lvItem.iImage = index;
320         lvItem.lParam = iter->id;
321
322         index = ListView_InsertItemW(hWnd, &lvItem);
323
324         /* now add the subitems (columns) */
325         ListView_SetItemTextW(hWnd, index, 1, iter->publisher);
326         ListView_SetItemTextW(hWnd, index, 2, iter->version);
327
328         iter = iter->next;
329     }
330 }
331
332 /******************************************************************************
333  * Name       : RemoveItemsFromList
334  * Description: Clears the application list box.
335  * Parameters : hWnd    - Handle of the dialog box
336  */
337 static void RemoveItemsFromList(HWND hWnd)
338 {
339     SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_DELETEALLITEMS, 0, 0);
340 }
341
342 /******************************************************************************
343  * Name       : EmptyList
344  * Description: Frees memory used by the application linked list.
345  */
346 static inline void EmptyList(void)
347 {
348     FreeAppInfo(AppInfo);
349     AppInfo = NULL;
350 }
351
352 /******************************************************************************
353  * Name       : UpdateButtons
354  * Description: Enables/disables the Add/Remove button depending on current
355  *              selection in list box.
356  * Parameters : hWnd    - Handle of the dialog box
357  */
358 static void UpdateButtons(HWND hWnd)
359 {
360     BOOL sel = ListView_GetSelectedCount(GetDlgItem(hWnd, IDL_PROGRAMS)) != 0;
361
362     EnableWindow(GetDlgItem(hWnd, IDC_ADDREMOVE), sel);
363     EnableWindow(GetDlgItem(hWnd, IDC_SUPPORT_INFO), sel);
364 }
365
366 /******************************************************************************
367  * Name       : UninstallProgram
368  * Description: Executes the specified program's installer.
369  * Parameters : id      - the internal ID of the installer to remove
370  */
371 static void UninstallProgram(int id)
372 {
373     APPINFO *iter;
374     STARTUPINFOW si;
375     PROCESS_INFORMATION info;
376     WCHAR errormsg[MAX_STRING_LEN];
377     WCHAR sUninstallFailed[MAX_STRING_LEN];
378     HKEY hkey;
379     BOOL res;
380
381     LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
382         sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
383
384     for (iter = AppInfo; iter; iter = iter->next)
385     {
386         if (iter->id == id)
387         {
388             TRACE("Uninstalling %s (%s)\n", wine_dbgstr_w(iter->title),
389                 wine_dbgstr_w(iter->path));
390
391             memset(&si, 0, sizeof(STARTUPINFOW));
392             si.cb = sizeof(STARTUPINFOW);
393             si.wShowWindow = SW_NORMAL;
394             res = CreateProcessW(NULL, iter->path, NULL, NULL, FALSE, 0, NULL,
395                 NULL, &si, &info);
396
397             if (res)
398             {
399                 /* wait for the process to exit */
400                 WaitForSingleObject(info.hProcess, INFINITE);
401             }
402             else
403             {
404                 wsprintfW(errormsg, sUninstallFailed, iter->path);
405
406                 if (MessageBoxW(0, errormsg, iter->title, MB_YESNO |
407                     MB_ICONQUESTION) == IDYES)
408                 {
409                     /* delete the application's uninstall entry */
410                     RegOpenKeyExW(iter->regroot, PathUninstallW, 0, KEY_READ, &hkey);
411                     RegDeleteKeyW(hkey, iter->regkey);
412                     RegCloseKey(hkey);
413                 }
414             }
415
416             break;
417         }
418     }
419 }
420
421 /******************************************************************************
422  * Name       : SupportInfoDlgProc
423  * Description: Callback procedure for support info dialog
424  * Parameters : hWnd    - hWnd of the window
425  *              msg     - reason for calling function
426  *              wParam  - additional parameter
427  *              lParam  - additional parameter
428  * Returns    : Dependant on message
429  */
430 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
431 {
432     APPINFO *iter;
433     WCHAR oldtitle[MAX_STRING_LEN];
434     WCHAR buf[MAX_STRING_LEN];
435
436     switch(msg)
437     {
438         case WM_INITDIALOG:
439             for (iter = AppInfo; iter; iter = iter->next)
440             {
441                 if (iter->id == (int) lParam)
442                 {
443                     /* Update the main label with the app name */
444                     if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
445                         MAX_STRING_LEN) != 0)
446                     {
447                         wsprintfW(buf, oldtitle, iter->title);
448                         SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
449                     }
450
451                     break;
452                 }
453             }
454
455             return TRUE;
456
457         case WM_DESTROY:
458             return 0;
459
460         case WM_COMMAND:
461             switch (LOWORD(wParam))
462             {
463                 case IDOK:
464                     EndDialog(hWnd, TRUE);
465                     break;
466
467             }
468
469             return TRUE;
470     }
471
472     return FALSE;
473 }
474
475 /******************************************************************************
476  * Name       : SupportInfo
477  * Description: Displays the Support Information dialog
478  * Parameters : hWnd    - Handle of the main dialog
479  *              id      - ID of the application to display information for
480  */
481 static void SupportInfo(HWND hWnd, int id)
482 {
483     DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
484         SupportInfoDlgProc, (LPARAM) id);
485 }
486
487 /* Definition of column headers for AddListViewColumns function */
488 typedef struct AppWizColumn {
489    int width;
490    int fmt;
491    int title;
492 } AppWizColumn;
493
494 AppWizColumn columns[] = {
495     {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
496     {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
497     {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
498 };
499
500 /******************************************************************************
501  * Name       : AddListViewColumns
502  * Description: Adds column headers to the list view control.
503  * Parameters : hWnd    - Handle of the list view control.
504  * Returns    : TRUE if completed successfully, FALSE otherwise.
505  */
506 static BOOL AddListViewColumns(HWND hWnd)
507 {
508     WCHAR buf[MAX_STRING_LEN];
509     LVCOLUMNW lvc;
510     int i;
511
512     lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
513
514     /* Add the columns */
515     for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
516     {
517         lvc.iSubItem = i;
518         lvc.pszText = buf;
519
520         /* set width and format */
521         lvc.cx = columns[i].width;
522         lvc.fmt = columns[i].fmt;
523
524         LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
525
526         if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
527             return FALSE;
528     }
529
530     return TRUE;
531 }
532
533 /******************************************************************************
534  * Name       : AddListViewImageList
535  * Description: Creates an ImageList for the list view control.
536  * Parameters : hWnd    - Handle of the list view control.
537  * Returns    : Handle of the image list.
538  */
539 static HIMAGELIST AddListViewImageList(HWND hWnd)
540 {
541     HIMAGELIST hSmall;
542     HICON hDefaultIcon;
543
544     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
545         ILC_MASK, 1, 1);
546
547     /* Add default icon to image list */
548     hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
549     ImageList_AddIcon(hSmall, hDefaultIcon);
550     DestroyIcon(hDefaultIcon);
551
552     (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
553
554     return hSmall;
555 }
556
557 /******************************************************************************
558  * Name       : ResetApplicationList
559  * Description: Empties the app list, if need be, and recreates it.
560  * Parameters : bFirstRun  - TRUE if this is the first time this is run, FALSE otherwise
561  *              hWnd       - handle of the dialog box
562  *              hImageList - handle of the image list
563  * Returns    : New handle of the image list.
564  */
565 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
566 {
567     HWND hWndListView;
568
569     hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
570
571     /* if first run, create the image list and add the listview columns */
572     if (bFirstRun)
573     {
574         if (!AddListViewColumns(hWndListView))
575             return NULL;
576     }
577     else /* we need to remove the existing things first */
578     {
579         RemoveItemsFromList(hWnd);
580         ImageList_Destroy(hImageList);
581
582         /* reset the list, since it's probably changed if the uninstallation was
583            successful */
584         EmptyList();
585     }
586
587     /* now create the image list and add the applications to the listview */
588     hImageList = AddListViewImageList(hWndListView);
589
590     ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
591     ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
592
593     AddApplicationsToList(hWndListView, hImageList);
594     UpdateButtons(hWnd);
595
596     return(hImageList);
597 }
598
599 /******************************************************************************
600  * Name       : MainDlgProc
601  * Description: Callback procedure for main tab
602  * Parameters : hWnd    - hWnd of the window
603  *              msg     - reason for calling function
604  *              wParam  - additional parameter
605  *              lParam  - additional parameter
606  * Returns    : Dependant on message
607  */
608 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
609 {
610     int selitem;
611     static HIMAGELIST hImageList;
612     LPNMHDR nmh;
613     LVITEMW lvItem;
614
615     switch(msg)
616     {
617         case WM_INITDIALOG:
618             hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
619
620             if (!hImageList)
621                 return FALSE;
622
623             return TRUE;
624
625         case WM_DESTROY:
626             RemoveItemsFromList(hWnd);
627             ImageList_Destroy(hImageList);
628
629             EmptyList();
630
631             return 0;
632
633         case WM_NOTIFY:
634             nmh = (LPNMHDR) lParam;
635
636             switch (nmh->idFrom)
637             {
638                 case IDL_PROGRAMS:
639                     switch (nmh->code)
640                     {
641                         case LVN_ITEMCHANGED:
642                             UpdateButtons(hWnd);
643                             break;
644                     }
645                     break;
646             }
647
648             return TRUE;
649
650         case WM_COMMAND:
651             switch (LOWORD(wParam))
652             {
653                 case IDC_ADDREMOVE:
654                     selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
655                         LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
656
657                     if (selitem != -1)
658                     {
659                         lvItem.iItem = selitem;
660                         lvItem.mask = LVIF_PARAM;
661
662                         if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
663                           0, (LPARAM) &lvItem))
664                             UninstallProgram(lvItem.lParam);
665                     }
666
667                     hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
668
669                     break;
670
671                 case IDC_SUPPORT_INFO:
672                     selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
673                         LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
674
675                     if (selitem != -1)
676                     {
677                         lvItem.iItem = selitem;
678                         lvItem.mask = LVIF_PARAM;
679
680                         if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
681                           0, (LPARAM) &lvItem))
682                             SupportInfo(hWnd, lvItem.lParam);
683                     }
684
685                     break;
686             }
687
688             return TRUE;
689     }
690
691     return FALSE;
692 }
693
694 /******************************************************************************
695  * Name       : StartApplet
696  * Description: Main routine for applet
697  * Parameters : hWnd    - hWnd of the Control Panel
698  */
699 static void StartApplet(HWND hWnd)
700 {
701     PROPSHEETPAGEW psp;
702     PROPSHEETHEADERW psh;
703     WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
704
705     /* Load the strings we will use */
706     LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
707     LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
708
709     /* Fill out the PROPSHEETPAGE */
710     psp.dwSize = sizeof (PROPSHEETPAGEW);
711     psp.dwFlags = PSP_USETITLE;
712     psp.hInstance = hInst;
713     psp.u.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
714     psp.u2.pszIcon = NULL;
715     psp.pfnDlgProc = (DLGPROC) MainDlgProc;
716     psp.pszTitle = tab_title;
717     psp.lParam = 0;
718
719     /* Fill out the PROPSHEETHEADER */
720     psh.dwSize = sizeof (PROPSHEETHEADERW);
721     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
722     psh.hwndParent = hWnd;
723     psh.hInstance = hInst;
724     psh.u.pszIcon = NULL;
725     psh.pszCaption = app_title;
726     psh.nPages = 1;
727     psh.u3.ppsp = &psp;
728     psh.pfnCallback = NULL;
729     psh.u2.nStartPage = 0;
730
731     /* Display the property sheet */
732     PropertySheetW (&psh);
733 }
734
735 /******************************************************************************
736  * Name       : CPlApplet
737  * Description: Entry point for Control Panel applets
738  * Parameters : hwndCPL - hWnd of the Control Panel
739  *              message - reason for calling function
740  *              lParam1 - additional parameter
741  *              lParam2 - additional parameter
742  * Returns    : Dependant on message
743  */
744 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
745 {
746     INITCOMMONCONTROLSEX iccEx;
747
748     switch (message)
749     {
750         case CPL_INIT:
751             iccEx.dwSize = sizeof(iccEx);
752             iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
753
754             InitCommonControlsEx(&iccEx);
755
756             return TRUE;
757
758         case CPL_GETCOUNT:
759             return 1;
760
761         case CPL_INQUIRE:
762         {
763             CPLINFO *appletInfo = (CPLINFO *) lParam2;
764
765             appletInfo->idIcon = ICO_MAIN;
766             appletInfo->idName = IDS_CPL_TITLE;
767             appletInfo->idInfo = IDS_CPL_DESC;
768             appletInfo->lData = 0;
769
770             break;
771         }
772
773         case CPL_DBLCLK:
774             StartApplet(hwndCPL);
775             break;
776     }
777
778     return FALSE;
779 }