appwiz.cpl: Add applications to list, remove on window close.
[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 #include "config.h"
27 #include "wine/port.h"
28 #include "wine/unicode.h"
29 #include "wine/debug.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 <cpl.h>
43
44 #include "res.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl);
47
48 /* define a maximum length for various buffers we use */
49 #define MAX_STRING_LEN    1024
50
51 typedef struct APPINFO {
52     int id;
53
54     LPWSTR title;
55     LPWSTR path;
56
57     LPWSTR icon;
58     int iconIdx;
59
60     LPWSTR publisher;
61     LPWSTR version;
62
63     HKEY regroot;
64     WCHAR regkey[MAX_STRING_LEN];
65
66     struct APPINFO *next;
67 } APPINFO;
68
69 static struct APPINFO *AppInfo = NULL;
70 static HINSTANCE hInst;
71
72 /* names of registry keys */
73 static const WCHAR BackSlashW[] = { '\\', 0 };
74 static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
75 static const WCHAR DisplayIconW[] = {'D','i','s','p','l','a','y','I','c','o','n',0};
76 static const WCHAR DisplayVersionW[] = {'D','i','s','p','l','a','y','V','e','r',
77     's','i','o','n',0};
78 static const WCHAR PublisherW[] = {'P','u','b','l','i','s','h','e','r',0};
79 static const WCHAR UninstallCommandlineW[] = {'U','n','i','n','s','t','a','l','l',
80     'S','t','r','i','n','g',0};
81
82 static const WCHAR PathUninstallW[] = {
83         'S','o','f','t','w','a','r','e','\\',
84         'M','i','c','r','o','s','o','f','t','\\',
85         'W','i','n','d','o','w','s','\\',
86         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
87         'U','n','i','n','s','t','a','l','l',0 };
88
89 /******************************************************************************
90  * Name       : DllMain
91  * Description: Entry point for DLL file
92  */
93 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
94                     LPVOID lpvReserved)
95 {
96     TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
97
98     switch (fdwReason)
99     {
100         case DLL_PROCESS_ATTACH:
101             hInst = hinstDLL;
102             break;
103     }
104     return TRUE;
105 }
106
107 /******************************************************************************
108  * Name       : FreeAppInfo
109  * Description: Frees memory used by an AppInfo structure, and any children.
110  */
111 static void FreeAppInfo(APPINFO *info)
112 {
113     APPINFO *iter = info;
114
115     while (iter)
116     {
117         if (iter->title)
118             HeapFree(GetProcessHeap(), 0, iter->title);
119
120         if (iter->path)
121             HeapFree(GetProcessHeap(), 0, iter->path);
122
123         if (iter->icon)
124             HeapFree(GetProcessHeap(), 0, iter->icon);
125
126         if (iter->publisher)
127             HeapFree(GetProcessHeap(), 0, iter->publisher);
128
129         if (iter->version)
130             HeapFree(GetProcessHeap(), 0, iter->version);
131
132         iter = iter->next;
133         HeapFree(GetProcessHeap(), 0, iter);
134     }
135 }
136
137 /******************************************************************************
138  * Name       : ReadApplicationsFromRegistry
139  * Description: Creates a linked list of uninstallable applications from the
140  *              registry.
141  * Parameters : root    - Which registry root to read from (HKCU/HKLM)
142  * Returns    : TRUE if successful, FALSE otherwise
143  */
144 static BOOL ReadApplicationsFromRegistry(HKEY root)
145 {
146     HKEY hkeyUninst, hkeyApp;
147     int i, id = 0;
148     DWORD sizeOfSubKeyName, displen, uninstlen;
149     WCHAR subKeyName[256];
150     WCHAR key_app[MAX_STRING_LEN];
151     WCHAR *p;
152     APPINFO *iter = AppInfo;
153     LPWSTR iconPtr;
154     BOOL ret = FALSE;
155
156     if (RegOpenKeyExW(root, PathUninstallW, 0, KEY_READ, &hkeyUninst) !=
157       ERROR_SUCCESS)
158         return FALSE;
159
160     lstrcpyW(key_app, PathUninstallW);
161     lstrcatW(key_app, BackSlashW);
162     p = key_app+lstrlenW(PathUninstallW)+1;
163
164     sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
165
166     if (iter)
167     {
168         /* find the end of the list */
169         for (iter = AppInfo; iter->next; iter = iter->next);
170     }
171
172     for (i = 0; RegEnumKeyExW(hkeyUninst, i, subKeyName, &sizeOfSubKeyName, NULL,
173         NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; ++i)
174     {
175         lstrcpyW(p, subKeyName);
176         RegOpenKeyExW(root, key_app, 0, KEY_READ, &hkeyApp);
177
178         displen = 0;
179         uninstlen = 0;
180
181         if ((RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, NULL, &displen) ==
182             ERROR_SUCCESS) && (RegQueryValueExW(hkeyApp, UninstallCommandlineW,
183             0, 0, NULL, &uninstlen) == ERROR_SUCCESS))
184         {
185             /* if we already have iter, allocate the next entry */
186             if (iter)
187             {
188                 iter->next = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
189                     sizeof(struct APPINFO));
190
191                 if (!iter->next)
192                     goto err;
193
194                 iter = iter->next;
195             }
196             else
197             {
198                 /* if not, start the list */
199                 iter = AppInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
200                     sizeof(struct APPINFO));
201
202                 if (!iter)
203                     goto err;
204             }
205
206             iter->title = HeapAlloc(GetProcessHeap(), 0, displen);
207
208             if (!iter->title)
209                 goto err;
210
211             RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0, (LPBYTE)iter->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                 iter->icon = 0;
220             else
221             {
222                 iter->icon = HeapAlloc(GetProcessHeap(), 0, displen);
223
224                 if (!iter->icon)
225                     goto err;
226
227                 RegQueryValueExW(hkeyApp, DisplayIconW, 0, 0, (LPBYTE)iter->icon,
228                     &displen);
229
230                 /* separate the index from the icon name, if supplied */
231                 iconPtr = strchrW(iter->icon, ',');
232
233                 if (iconPtr)
234                 {
235                     *iconPtr++ = 0;
236                     iter->iconIdx = atoiW(iconPtr);
237                 }
238             }
239
240             iter->path = HeapAlloc(GetProcessHeap(), 0, uninstlen);
241
242             if (!iter->path)
243                 goto err;
244
245             RegQueryValueExW(hkeyApp, UninstallCommandlineW, 0, 0,
246                 (LPBYTE)iter->path, &uninstlen);
247
248             /* publisher, version */
249             if (RegQueryValueExW(hkeyApp, PublisherW, 0, 0, NULL, &displen) ==
250                 ERROR_SUCCESS)
251             {
252                 iter->publisher = HeapAlloc(GetProcessHeap(), 0, displen);
253
254                 if (!iter->publisher)
255                     goto err;
256
257                 RegQueryValueExW(hkeyApp, PublisherW, 0, 0, (LPBYTE)iter->publisher,
258                     &displen);
259             }
260
261             if (RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, NULL, &displen) ==
262                 ERROR_SUCCESS)
263             {
264                 iter->version = HeapAlloc(GetProcessHeap(), 0, displen);
265
266                 if (!iter->version)
267                     goto err;
268
269                 RegQueryValueExW(hkeyApp, DisplayVersionW, 0, 0, (LPBYTE)iter->version,
270                     &displen);
271             }
272
273             /* registry key */
274             iter->regroot = root;
275             lstrcpyW(iter->regkey, subKeyName);
276
277             iter->id = id++;
278         }
279
280         RegCloseKey(hkeyApp);
281         sizeOfSubKeyName = sizeof(subKeyName) / sizeof(subKeyName[0]);
282     }
283
284     ret = TRUE;
285     goto end;
286
287 err:
288     RegCloseKey(hkeyApp);
289     FreeAppInfo(iter);
290
291 end:
292     RegCloseKey(hkeyUninst);
293     return ret;
294 }
295
296
297 /******************************************************************************
298  * Name       : AddApplicationsToList
299  * Description: Populates the list box with applications.
300  * Parameters : hWnd    - Handle of the dialog box
301  */
302 static void AddApplicationsToList(HWND hWnd, HIMAGELIST hList)
303 {
304     APPINFO *iter = AppInfo;
305     LVITEMW lvItem;
306     HICON hIcon;
307     int index;
308
309     while (iter)
310     {
311         /* get the icon */
312         index = 0;
313
314         if (iter->icon)
315         {
316             if (ExtractIconExW(iter->icon, iter->iconIdx, NULL, &hIcon, 1) == 1)
317             {
318                 index = ImageList_AddIcon(hList, hIcon);
319                 DestroyIcon(hIcon);
320             }
321         }
322
323         lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
324         lvItem.iItem = iter->id;
325         lvItem.iSubItem = 0;
326         lvItem.pszText = iter->title;
327         lvItem.iImage = index;
328         lvItem.lParam = iter->id;
329
330         index = ListView_InsertItemW(hWnd, &lvItem);
331
332         /* now add the subitems (columns) */
333         ListView_SetItemTextW(hWnd, index, 1, iter->publisher);
334         ListView_SetItemTextW(hWnd, index, 2, iter->version);
335
336         iter = iter->next;
337     }
338 }
339
340 /******************************************************************************
341  * Name       : RemoveItemsFromList
342  * Description: Clears the application list box.
343  * Parameters : hWnd    - Handle of the dialog box
344  */
345 static void RemoveItemsFromList(HWND hWnd)
346 {
347     SendDlgItemMessageW(hWnd, IDL_PROGRAMS, LVM_DELETEALLITEMS, 0, 0);
348 }
349
350 /******************************************************************************
351  * Name       : EmptyList
352  * Description: Frees memory used by the application linked list.
353  */
354 static inline void EmptyList(void)
355 {
356     FreeAppInfo(AppInfo);
357     AppInfo = NULL;
358 }
359
360 /******************************************************************************
361  * Name       : UpdateButtons
362  * Description: Enables/disables the Add/Remove button depending on current
363  *              selection in list box.
364  * Parameters : hWnd    - Handle of the dialog box
365  */
366 static void UpdateButtons(HWND hWnd)
367 {
368     BOOL sel = ListView_GetSelectedCount(GetDlgItem(hWnd, IDL_PROGRAMS)) != 0;
369
370     EnableWindow(GetDlgItem(hWnd, IDC_ADDREMOVE), sel);
371     EnableWindow(GetDlgItem(hWnd, IDC_SUPPORT_INFO), sel);
372 }
373
374 /* Definition of column headers for AddListViewColumns function */
375 typedef struct AppWizColumn {
376    int width;
377    int fmt;
378    int title;
379 } AppWizColumn;
380
381 AppWizColumn columns[] = {
382     {200, LVCFMT_LEFT, IDS_COLUMN_NAME},
383     {150, LVCFMT_LEFT, IDS_COLUMN_PUBLISHER},
384     {100, LVCFMT_LEFT, IDS_COLUMN_VERSION},
385 };
386
387 /******************************************************************************
388  * Name       : AddListViewColumns
389  * Description: Adds column headers to the list view control.
390  * Parameters : hWnd    - Handle of the list view control.
391  * Returns    : TRUE if completed successfully, FALSE otherwise.
392  */
393 static BOOL AddListViewColumns(HWND hWnd)
394 {
395     WCHAR buf[MAX_STRING_LEN];
396     LVCOLUMNW lvc;
397     int i;
398
399     lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
400
401     /* Add the columns */
402     for (i = 0; i < sizeof(columns) / sizeof(columns[0]); i++)
403     {
404         lvc.iSubItem = i;
405         lvc.pszText = buf;
406
407         /* set width and format */
408         lvc.cx = columns[i].width;
409         lvc.fmt = columns[i].fmt;
410
411         LoadStringW(hInst, columns[i].title, buf, sizeof(buf) / sizeof(buf[0]));
412
413         if (ListView_InsertColumnW(hWnd, i, &lvc) == -1)
414             return FALSE;
415     }
416
417     return TRUE;
418 }
419
420 /******************************************************************************
421  * Name       : AddListViewImageList
422  * Description: Creates an ImageList for the list view control.
423  * Parameters : hWnd    - Handle of the list view control.
424  * Returns    : Handle of the image list.
425  */
426 static HIMAGELIST AddListViewImageList(HWND hWnd)
427 {
428     HIMAGELIST hSmall;
429     HICON hDefaultIcon;
430
431     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
432         ILC_MASK, 1, 1);
433
434     /* Add default icon to image list */
435     hDefaultIcon = LoadIconW(hInst, MAKEINTRESOURCEW(ICO_MAIN));
436     ImageList_AddIcon(hSmall, hDefaultIcon);
437     DestroyIcon(hDefaultIcon);
438
439     (void) ListView_SetImageList(hWnd, hSmall, LVSIL_SMALL);
440
441     return hSmall;
442 }
443
444 /******************************************************************************
445  * Name       : ResetApplicationList
446  * Description: Empties the app list, if need be, and recreates it.
447  * Parameters : bFirstRun  - TRUE if this is the first time this is run, FALSE otherwise
448  *              hWnd       - handle of the dialog box
449  *              hImageList - handle of the image list
450  * Returns    : New handle of the image list.
451  */
452 static HIMAGELIST ResetApplicationList(BOOL bFirstRun, HWND hWnd, HIMAGELIST hImageList)
453 {
454     HWND hWndListView;
455
456     hWndListView = GetDlgItem(hWnd, IDL_PROGRAMS);
457
458     /* if first run, create the image list and add the listview columns */
459     if (bFirstRun)
460     {
461         if (!AddListViewColumns(hWndListView))
462             return NULL;
463     }
464     else /* we need to remove the existing things first */
465     {
466         RemoveItemsFromList(hWnd);
467         ImageList_Destroy(hImageList);
468
469         /* reset the list, since it's probably changed if the uninstallation was
470            successful */
471         EmptyList();
472     }
473
474     /* now create the image list and add the applications to the listview */
475     hImageList = AddListViewImageList(hWndListView);
476
477     ReadApplicationsFromRegistry(HKEY_LOCAL_MACHINE);
478     ReadApplicationsFromRegistry(HKEY_CURRENT_USER);
479
480     AddApplicationsToList(hWndListView, hImageList);
481     UpdateButtons(hWnd);
482
483     return(hImageList);
484 }
485
486 /******************************************************************************
487  * Name       : MainDlgProc
488  * Description: Callback procedure for main tab
489  * Parameters : hWnd    - hWnd of the window
490  *              msg     - reason for calling function
491  *              wParam  - additional parameter
492  *              lParam  - additional parameter
493  * Returns    : Dependant on message
494  */
495 static BOOL CALLBACK MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
496 {
497     static HIMAGELIST hImageList;
498     LPNMHDR nmh;
499
500     switch(msg)
501     {
502         case WM_INITDIALOG:
503             hImageList = ResetApplicationList(TRUE, hWnd, hImageList);
504
505             if (!hImageList)
506                 return FALSE;
507
508             return TRUE;
509
510         case WM_DESTROY:
511             RemoveItemsFromList(hWnd);
512             ImageList_Destroy(hImageList);
513
514             EmptyList();
515
516             return 0;
517
518         case WM_NOTIFY:
519             nmh = (LPNMHDR) lParam;
520
521             switch (nmh->idFrom)
522             {
523                 case IDL_PROGRAMS:
524                     switch (nmh->code)
525                     {
526                         case LVN_ITEMCHANGED:
527                             UpdateButtons(hWnd);
528                             break;
529                     }
530                     break;
531             }
532
533             return TRUE;
534     }
535
536     return FALSE;
537 }
538
539 /******************************************************************************
540  * Name       : StartApplet
541  * Description: Main routine for applet
542  * Parameters : hWnd    - hWnd of the Control Panel
543  */
544 static void StartApplet(HWND hWnd)
545 {
546     PROPSHEETPAGEW psp;
547     PROPSHEETHEADERW psh;
548     WCHAR tab_title[MAX_STRING_LEN], app_title[MAX_STRING_LEN];
549
550     /* Load the strings we will use */
551     LoadStringW(hInst, IDS_TAB1_TITLE, tab_title, sizeof(tab_title) / sizeof(tab_title[0]));
552     LoadStringW(hInst, IDS_CPL_TITLE, app_title, sizeof(app_title) / sizeof(app_title[0]));
553
554     /* Fill out the PROPSHEETPAGE */
555     psp.dwSize = sizeof (PROPSHEETPAGEW);
556     psp.dwFlags = PSP_USETITLE;
557     psp.hInstance = hInst;
558     psp.pszTemplate = MAKEINTRESOURCEW (IDD_MAIN);
559     psp.pszIcon = NULL;
560     psp.pfnDlgProc = (DLGPROC) MainDlgProc;
561     psp.pszTitle = tab_title;
562     psp.lParam = 0;
563
564     /* Fill out the PROPSHEETHEADER */
565     psh.dwSize = sizeof (PROPSHEETHEADERW);
566     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID;
567     psh.hwndParent = hWnd;
568     psh.hInstance = hInst;
569     psh.pszIcon = NULL;
570     psh.pszCaption = app_title;
571     psh.nPages = 1;
572     psh.ppsp = &psp;
573     psh.pfnCallback = NULL;
574     psh.nStartPage = 0;
575
576     /* Display the property sheet */
577     PropertySheetW (&psh);
578 }
579
580 /******************************************************************************
581  * Name       : CPlApplet
582  * Description: Entry point for Control Panel applets
583  * Parameters : hwndCPL - hWnd of the Control Panel
584  *              message - reason for calling function
585  *              lParam1 - additional parameter
586  *              lParam2 - additional parameter
587  * Returns    : Dependant on message
588  */
589 LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2)
590 {
591     INITCOMMONCONTROLSEX iccEx;
592
593     switch (message)
594     {
595         case CPL_INIT:
596             iccEx.dwSize = sizeof(iccEx);
597             iccEx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
598
599             InitCommonControlsEx(&iccEx);
600
601             return TRUE;
602
603         case CPL_GETCOUNT:
604             return 1;
605
606         case CPL_INQUIRE:
607         {
608             CPLINFO *appletInfo = (CPLINFO *) lParam2;
609
610             appletInfo->idIcon = ICO_MAIN;
611             appletInfo->idName = IDS_CPL_TITLE;
612             appletInfo->idInfo = IDS_CPL_DESC;
613             appletInfo->lData = 0;
614
615             break;
616         }
617
618         case CPL_DBLCLK:
619             StartApplet(hwndCPL);
620             break;
621     }
622
623     return FALSE;
624 }