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