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