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