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