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