appwiz.cpl: Added Dutch resources.
[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 ContactW[] = {'C','o','n','t','a','c','t',0};
82 static const WCHAR HelpLinkW[] = {'H','e','l','p','L','i','n','k',0};
83 static const WCHAR HelpTelephoneW[] = {'H','e','l','p','T','e','l','e','p','h',
84     'o','n','e',0};
85 static const WCHAR ReadmeW[] = {'R','e','a','d','m','e',0};
86 static const WCHAR URLUpdateInfoW[] = {'U','R','L','U','p','d','a','t','e','I',
87     'n','f','o',0};
88 static const WCHAR CommentsW[] = {'C','o','m','m','e','n','t','s',0};
89 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
90     'S','t','r','i','n','g',0};
91
92 static const WCHAR PathUninstallW[] = {
93         'S','o','f','t','w','a','r','e','\\',
94         'M','i','c','r','o','s','o','f','t','\\',
95         'W','i','n','d','o','w','s','\\',
96         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
97         'U','n','i','n','s','t','a','l','l',0 };
98
99 /******************************************************************************
100  * Name       : DllMain
101  * Description: Entry point for DLL file
102  */
103 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
104                     LPVOID lpvReserved)
105 {
106     TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
107
108     switch (fdwReason)
109     {
110         case DLL_PROCESS_ATTACH:
111             hInst = hinstDLL;
112             break;
113     }
114     return TRUE;
115 }
116
117 /******************************************************************************
118  * Name       : FreeAppInfo
119  * Description: Frees memory used by an AppInfo structure, and any children.
120  */
121 static void FreeAppInfo(APPINFO *info)
122 {
123     while (info)
124     {
125         APPINFO *next_info = info->next;
126
127         HeapFree(GetProcessHeap(), 0, info->title);
128         HeapFree(GetProcessHeap(), 0, info->path);
129         HeapFree(GetProcessHeap(), 0, info->icon);
130         HeapFree(GetProcessHeap(), 0, info->publisher);
131         HeapFree(GetProcessHeap(), 0, info->version);
132         HeapFree(GetProcessHeap(), 0, info);
133         info = next_info;
134     }
135 }
136
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
143  */
144 static BOOL ReadApplicationsFromRegistry(HKEY root)
145 {
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;
155
156     if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
157       ERROR_SUCCESS)
158         return FALSE;
159
160     lstrcpyW(key_app, PathUninstallW);
161     lstrcatW(key_app, BackSlashW);
162     p = key_app+lstrlenW(PathUninstallW)+1;
163
164     sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
165
166     if (iter)
167     {
168         /* find the end of the list */
169         for (iter = AppInfo; iter->next; iter = iter->next);
170     }
171
172     for (i = 0; RegEnumKeyExW(hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL,
173         NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; ++i)
174     {
175         lstrcpyW(p, subKeyName);
176         RegOpenKeyExW(root, key_app, 0, KEY_READ, &hkeyApp);
177
178         displen = 0;
179         uninstlen = 0;
180
181         if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) ==
182             ERROR_SUCCESS) && (RegQueryValueExW(hkeyApp, UninstallCommandlineW,
183             0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
184         {
185             /* if we already have iter, allocate the next entry */
186             if (iter)
187             {
188                 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
189                     sizeof(struct APPINFO));
190
191                 if (!iter->next)
192                     goto err;
193
194                 iter = iter->next;
195             }
196             else
197             {
198                 /* if not, start the list */
199                 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
200                     sizeof(struct APPINFO));
201
202                 if (!iter)
203                     goto err;
204             }
205
206             iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
207
208             if (!iter->title)
209                 goto err;
210
211             RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->title,
212                 &displen);
213
214             /* now get DisplayIcon */
215             displen = 0;
216             RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, NULL, &displen);
217
218             if (displen == 0)
219                 iter->icon = 0;
220             else
221             {
222                 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
223
224                 if (!iter->icon)
225                     goto err;
226
227                 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
228                     &displen);
229
230                 /* separate the index from the icon name, if supplied */
231                 iconPtr = strchrW(iter->icon, ',');
232
233                 if (iconPtr)
234                 {
235                     *iconPtr++ = 0;
236                     iter->iconIdx = atoiW(iconPtr);
237                 }
238             }
239
240             iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
241
242             if (!iter->path)
243                 goto err;
244
245             RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
246                 (LPBYTE)iter->path, &uninstlen);
247
248             /* publisher, version */
249             if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
250                 ERROR_SUCCESS)
251             {
252                 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
253
254                 if (!iter->publisher)
255                     goto err;
256
257                 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
258                     &displen);
259             }
260
261             if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
262                 ERROR_SUCCESS)
263             {
264                 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
265
266                 if (!iter->version)
267                     goto err;
268
269                 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
270                     &displen);
271             }
272
273             /* registry key */
274             iter->regroot = root;
275             lstrcpyW(iter->regkey, subKeyName);
276
277             iter->id = id++;
278         }
279
280         RegCloseKey(hkeyApp);
281         sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
282     }
283
284     ret = TRUE;
285     goto end;
286
287 err:
288     RegCloseKey(hkeyApp);
289     FreeAppInfo(iter);
290
291 end:
292     RegCloseKey(hkeyUninst);
293     return ret;
294 }
295
296
297 /******************************************************************************
298  * Name       : AddApplicationsToList
299  * Description: Populates the list box with applications.
300  * Parameters : hWnd    - Handle of the dialog box
301  */
302 static void AddApplicationsToList(HWND hWnd, HIMAGELIST hList)
303 {
304     APPINFO *iter = AppInfo;
305     LVITEMW lvItem;
306     HICON hIcon;
307     int index;
308
309     while (iter)
310     {
311         /* get the icon */
312         index = 0;
313
314         if (iter->icon)
315         {
316             if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
317             {
318                 index = ImageList_AddIcon(hList, hIcon);
319                 DestroyIcon(hIcon);
320             }
321         }
322
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;
329
330         index = ListView_InsertItemW(hWnd, &lvItem);
331
332         /* now add the subitems (columns) */
333         ListView_SetItemTextW(hWnd, index, 1, iter->publisher);
334         ListView_SetItemTextW(hWnd, index, 2, iter->version);
335
336         iter = iter->next;
337     }
338 }
339
340 /******************************************************************************
341  * Name       : RemoveItemsFromList
342  * Description: Clears the application list box.
343  * Parameters : hWnd    - Handle of the dialog box
344  */
345 static void RemoveItemsFromList(HWND hWnd)
346 {
347     SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_DELETEALLITEMS, 0, 0);
348 }
349
350 /******************************************************************************
351  * Name       : EmptyList
352  * Description: Frees memory used by the application linked list.
353  */
354 static inline void EmptyList(void)
355 {
356     FreeAppInfo(AppInfo);
357     AppInfo = NULL;
358 }
359
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
365  */
366 static void UpdateButtons(HWND hWnd)
367 {
368     BOOL sel = ListView_GetSelectedCount(GetDlgItem(hWnd, IDL_PROGRAMS)) != 0;
369
370     EnableWindow(GetDlgItem(hWnd, IDC_ADDREMOVE), sel);
371     EnableWindow(GetDlgItem(hWnd, IDC_SUPPORT_INFO), sel);
372 }
373
374 /******************************************************************************
375  * Name       : UninstallProgram
376  * Description: Executes the specified program's installer.
377  * Parameters : id      - the internal ID of the installer to remove
378  */
379 static void UninstallProgram(int id)
380 {
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;
388
389     LoadStringW(hInst, IDS_UNINSTALL_FAILED, sUninstallFailed,
390         sizeof(sUninstallFailed) / sizeof(sUninstallFailed[0]));
391
392     for (iter = AppInfo; iter; iter = iter->next)
393     {
394         if (iter->id == id)
395         {
396             TRACE("Uninstalling %s (%s)\n", wine_dbgstr_w(iter->title),
397                 wine_dbgstr_w(iter->path));
398
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);
404
405             if (res)
406             {
407                 /* wait for the process to exit */
408                 WaitForSingleObject(info.hProcess, INFINITE);
409             }
410             else
411             {
412                 wsprintfW(errormsg, sUninstallFailed, iter->path);
413
414                 if (MessageBoxW(0, errormsg, iter->title, MB_YESNO |
415                     MB_ICONQUESTION) == IDYES)
416                 {
417                     /* delete the application's uninstall entry */
418                     RegOpenKeyExW(iter->regroot, PathUninstallW, 0, KEY_READ, &hkey);
419                     RegDeleteKeyW(hkey, iter->regkey);
420                     RegCloseKey(hkey);
421                 }
422             }
423
424             break;
425         }
426     }
427 }
428
429 /**********************************************************************************
430  * Name       : SetInfoDialogText
431  * Description: Sets the text of a label in a window, based upon a registry entry
432  *              or string passed to the function.
433  * Parameters : hKey         - registry entry to read from, NULL if not reading
434  *                             from registry
435  *              lpKeyName    - key to read from, or string to check if hKey is NULL
436  *              lpAltMessage - alternative message if entry not found
437  *              hWnd         - handle of dialog box
438  *              iDlgItem     - ID of label in dialog box
439  */
440 static void SetInfoDialogText(HKEY hKey, LPWSTR lpKeyName, LPWSTR lpAltMessage,
441   HWND hWnd, int iDlgItem)
442 {
443     WCHAR buf[MAX_STRING_LEN];
444     DWORD buflen;
445     HWND hWndDlgItem;
446
447     hWndDlgItem = GetDlgItem(hWnd, iDlgItem);
448
449     /* if hKey is null, lpKeyName contains the string we want to check */
450     if (hKey == NULL)
451     {
452         if ((lpKeyName) && (lstrlenW(lpKeyName) > 0))
453             SetWindowTextW(hWndDlgItem, lpKeyName);
454         else
455             SetWindowTextW(hWndDlgItem, lpAltMessage);
456     }
457     else
458     {
459         buflen = MAX_STRING_LEN;
460
461         if ((RegQueryValueExW(hKey, lpKeyName, 0, 0, (LPBYTE) buf, &buflen) ==
462            ERROR_SUCCESS) && (lstrlenW(buf) > 0))
463             SetWindowTextW(hWndDlgItem, buf);
464         else
465             SetWindowTextW(hWndDlgItem, lpAltMessage);
466     }
467 }
468
469 /******************************************************************************
470  * Name       : SupportInfoDlgProc
471  * Description: Callback procedure for support info dialog
472  * Parameters : hWnd    - hWnd of the window
473  *              msg     - reason for calling function
474  *              wParam  - additional parameter
475  *              lParam  - additional parameter
476  * Returns    : Dependant on message
477  */
478 static BOOL CALLBACK SupportInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
479 {
480     APPINFO *iter;
481     HKEY hkey;
482     WCHAR oldtitle[MAX_STRING_LEN];
483     WCHAR buf[MAX_STRING_LEN];
484     WCHAR key[MAX_STRING_LEN];
485     WCHAR notfound[MAX_STRING_LEN];
486
487     switch(msg)
488     {
489         case WM_INITDIALOG:
490             for (iter = AppInfo; iter; iter = iter->next)
491             {
492                 if (iter->id == (int) lParam)
493                 {
494                     lstrcpyW(key, PathUninstallW);
495                     lstrcatW(key, BackSlashW);
496                     lstrcatW(key, iter->regkey);
497
498                     /* check the application's registry entries */
499                     RegOpenKeyExW(iter->regroot, key, 0, KEY_READ, &hkey);
500
501                     /* Load our "not specified" string */
502                     LoadStringW(hInst, IDS_NOT_SPECIFIED, notfound,
503                         sizeof(notfound) / sizeof(notfound[0]));
504
505                     /* Update the data for items already read into the structure */
506                     SetInfoDialogText(NULL, iter->publisher, notfound, hWnd,
507                         IDC_INFO_PUBLISHER);
508                     SetInfoDialogText(NULL, iter->version, notfound, hWnd,
509                         IDC_INFO_VERSION);
510
511                     /* And now update the data for those items in the registry */
512                     SetInfoDialogText(hkey, (LPWSTR) ContactW, notfound, hWnd,
513                         IDC_INFO_CONTACT);
514                     SetInfoDialogText(hkey, (LPWSTR) HelpLinkW, notfound, hWnd,
515                         IDC_INFO_SUPPORT);
516                     SetInfoDialogText(hkey, (LPWSTR) HelpTelephoneW, notfound, hWnd,
517                         IDC_INFO_PHONE);
518                     SetInfoDialogText(hkey, (LPWSTR) ReadmeW, notfound, hWnd,
519                         IDC_INFO_README);
520                     SetInfoDialogText(hkey, (LPWSTR) URLUpdateInfoW, notfound, hWnd,
521                         IDC_INFO_UPDATES);
522                     SetInfoDialogText(hkey, (LPWSTR) CommentsW, notfound, hWnd,
523                         IDC_INFO_COMMENTS);
524
525                     /* Update the main label with the app name */
526                     if (GetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), oldtitle,
527                         MAX_STRING_LEN) != 0)
528                     {
529                         wsprintfW(buf, oldtitle, iter->title);
530                         SetWindowTextW(GetDlgItem(hWnd, IDC_INFO_LABEL), buf);
531                     }
532
533                     RegCloseKey(hkey);
534
535                     break;
536                 }
537             }
538
539             return TRUE;
540
541         case WM_DESTROY:
542             return 0;
543
544         case WM_COMMAND:
545             switch (LOWORD(wParam))
546             {
547                 case IDOK:
548                     EndDialog(hWnd, TRUE);
549                     break;
550
551             }
552
553             return TRUE;
554     }
555
556     return FALSE;
557 }
558
559 /******************************************************************************
560  * Name       : SupportInfo
561  * Description: Displays the Support Information dialog
562  * Parameters : hWnd    - Handle of the main dialog
563  *              id      - ID of the application to display information for
564  */
565 static void SupportInfo(HWND hWnd, int id)
566 {
567     DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_INFO), hWnd, (DLGPROC)
568         SupportInfoDlgProc, (LPARAM) id);
569 }
570
571 /* Definition of column headers for AddListViewColumns function */
572 typedef struct AppWizColumn {
573    int width;
574    int fmt;
575    int title;
576 } AppWizColumn;
577
578 AppWizColumn columns[] = {
579     {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
580     {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
581     {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
582 };
583
584 /******************************************************************************
585  * Name       : AddListViewColumns
586  * Description: Adds column headers to the list view control.
587  * Parameters : hWnd    - Handle of the list view control.
588  * Returns    : TRUE if completed successfully, FALSE otherwise.
589  */
590 static BOOL AddListViewColumns(HWND hWnd)
591 {
592     WCHAR buf[MAX_STRING_LEN];
593     LVCOLUMNW lvc;
594     UINT i;
595
596     lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
597
598     /* Add the columns */
599     for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
600     {
601         lvc.iSubItem = i;
602         lvc.pszText = buf;
603
604         /* set width and format */
605         lvc.cx = columns[i].width;
606         lvc.fmt = columns[i].fmt;
607
608         LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
609
610         if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
611             return FALSE;
612     }
613
614     return TRUE;
615 }
616
617 /******************************************************************************
618  * Name       : AddListViewImageList
619  * Description: Creates an ImageList for the list view control.
620  * Parameters : hWnd    - Handle of the list view control.
621  * Returns    : Handle of the image list.
622  */
623 static HIMAGELIST AddListViewImageList(HWND hWnd)
624 {
625     HIMAGELIST hSmall;
626     HICON hDefaultIcon;
627
628     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
629         ILC_MASK, 1, 1);
630
631     /* Add default icon to image list */
632     hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
633     ImageList_AddIcon(hSmall, hDefaultIcon);
634     DestroyIcon(hDefaultIcon);
635
636     (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
637
638     return hSmall;
639 }
640
641 /******************************************************************************
642  * Name       : ResetApplicationList
643  * Description: Empties the app list, if need be, and recreates it.
644  * Parameters : bFirstRun  - TRUE if this is the first time this is run, FALSE otherwise
645  *              hWnd       - handle of the dialog box
646  *              hImageList - handle of the image list
647  * Returns    : New handle of the image list.
648  */
649 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
650 {
651     HWND hWndListView;
652
653     hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
654
655     /* if first run, create the image list and add the listview columns */
656     if (bFirstRun)
657     {
658         if (!AddListViewColumns(hWndListView))
659             return NULL;
660     }
661     else /* we need to remove the existing things first */
662     {
663         RemoveItemsFromList(hWnd);
664         ImageList_Destroy(hImageList);
665
666         /* reset the list, since it's probably changed if the uninstallation was
667            successful */
668         EmptyList();
669     }
670
671     /* now create the image list and add the applications to the listview */
672     hImageList = AddListViewImageList(hWndListView);
673
674     ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
675     ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
676
677     AddApplicationsToList(hWndListView, hImageList);
678     UpdateButtons(hWnd);
679
680     return(hImageList);
681 }
682
683 /******************************************************************************
684  * Name       : MainDlgProc
685  * Description: Callback procedure for main tab
686  * Parameters : hWnd    - hWnd of the window
687  *              msg     - reason for calling function
688  *              wParam  - additional parameter
689  *              lParam  - additional parameter
690  * Returns    : Dependant on message
691  */
692 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
693 {
694     int selitem;
695     static HIMAGELIST hImageList;
696     LPNMHDR nmh;
697     LVITEMW lvItem;
698
699     switch(msg)
700     {
701         case WM_INITDIALOG:
702             hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
703
704             if (!hImageList)
705                 return FALSE;
706
707             return TRUE;
708
709         case WM_DESTROY:
710             RemoveItemsFromList(hWnd);
711             ImageList_Destroy(hImageList);
712
713             EmptyList();
714
715             return 0;
716
717         case WM_NOTIFY:
718             nmh = (LPNMHDR) lParam;
719
720             switch (nmh->idFrom)
721             {
722                 case IDL_PROGRAMS:
723                     switch (nmh->code)
724                     {
725                         case LVN_ITEMCHANGED:
726                             UpdateButtons(hWnd);
727                             break;
728                     }
729                     break;
730             }
731
732             return TRUE;
733
734         case WM_COMMAND:
735             switch (LOWORD(wParam))
736             {
737                 case IDC_ADDREMOVE:
738                     selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
739                         LVM_GETNEXTITEM, -1, LVNI_FOCUSED|LVNI_SELECTED);
740
741                     if (selitem != -1)
742                     {
743                         lvItem.iItem = selitem;
744                         lvItem.mask = LVIF_PARAM;
745
746                         if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
747                           0, (LPARAM) &lvItem))
748                             UninstallProgram(lvItem.lParam);
749                     }
750
751                     hImageList = ResetApplicationList(FALSE, hWnd, hImageList);
752
753                     break;
754
755                 case IDC_SUPPORT_INFO:
756                     selitem = SendDlgItemMessageW(hWnd, IDL_PROGRAMS,
757                         LVM_GETNEXTITEM, -1, LVNI_FOCUSED | LVNI_SELECTED);
758
759                     if (selitem != -1)
760                     {
761                         lvItem.iItem = selitem;
762                         lvItem.mask = LVIF_PARAM;
763
764                         if (SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_GETITEMW,
765                           0, (LPARAM) &lvItem))
766                             SupportInfo(hWnd, lvItem.lParam);
767                     }
768
769                     break;
770             }
771
772             return TRUE;
773     }
774
775     return FALSE;
776 }
777
778 /******************************************************************************
779  * Name       : StartApplet
780  * Description: Main routine for applet
781  * Parameters : hWnd    - hWnd of the Control Panel
782  */
783 static void StartApplet(HWND hWnd)
784 {
785     PROPSHEETPAGEW psp;
786     PROPSHEETHEADERW psh;
787     WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
788
789     /* Load the strings we will use */
790     LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
791     LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
792
793     /* Fill out the PROPSHEETPAGE */
794     psp.dwSize = sizeof (PROPSHEETPAGEW);
795     psp.dwFlags = PSP_USETITLE;
796     psp.hInstance = hInst;
797     psp.u.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
798     psp.u2.pszIcon = NULL;
799     psp.pfnDlgProc = (DLGPROC) MainDlgProc;
800     psp.pszTitle = tab_title;
801     psp.lParam = 0;
802
803     /* Fill out the PROPSHEETHEADER */
804     psh.dwSize = sizeof (PROPSHEETHEADERW);
805     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
806     psh.hwndParent = hWnd;
807     psh.hInstance = hInst;
808     psh.u.pszIcon = NULL;
809     psh.pszCaption = app_title;
810     psh.nPages = 1;
811     psh.u3.ppsp = &psp;
812     psh.pfnCallback = NULL;
813     psh.u2.nStartPage = 0;
814
815     /* Display the property sheet */
816     PropertySheetW (&psh);
817 }
818
819 /******************************************************************************
820  * Name       : CPlApplet
821  * Description: Entry point for Control Panel applets
822  * Parameters : hwndCPL - hWnd of the Control Panel
823  *              message - reason for calling function
824  *              lParam1 - additional parameter
825  *              lParam2 - additional parameter
826  * Returns    : Dependant on message
827  */
828 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
829 {
830     INITCOMMONCONTROLSEX iccEx;
831
832     switch (message)
833     {
834         case CPL_INIT:
835             iccEx.dwSize = sizeof(iccEx);
836             iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
837
838             InitCommonControlsEx(&iccEx);
839
840             return TRUE;
841
842         case CPL_GETCOUNT:
843             return 1;
844
845         case CPL_INQUIRE:
846         {
847             CPLINFO *appletInfo = (CPLINFO *) lParam2;
848
849             appletInfo->idIcon = ICO_MAIN;
850             appletInfo->idName = IDS_CPL_TITLE;
851             appletInfo->idInfo = IDS_CPL_DESC;
852             appletInfo->lData = 0;
853
854             break;
855         }
856
857         case CPL_DBLCLK:
858             StartApplet(hwndCPL);
859             break;
860     }
861
862     return FALSE;
863 }