xref: /reactos/dll/cpl/hotplug/hotplug.c (revision 6a6b5ec2)
1 /*
2  * PROJECT:     Safely Remove Hardware Applet
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Applet initialization
5  * COPYRIGHT:   Copyright 2013 Johannes Anderwald <johannes.anderwald@reactos.org>
6  *              Copyright 2020 Eric Kohl <eric.kohl@reactos.org>
7  */
8 
9 #include "hotplug.h"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 // globals
15 HINSTANCE hApplet = 0;
16 
17 /* Applets */
18 APPLET Applets[NUM_APPLETS] =
19 {
20     {IDI_HOTPLUG, IDS_CPLNAME, IDS_CPLDESCRIPTION, InitApplet}
21 };
22 
23 static
24 DWORD
25 GetHotPlugFlags(VOID)
26 {
27     HKEY hKey = NULL;
28     DWORD dwFlags = 0;
29     DWORD dwSize, dwError;
30 
31     dwError = RegOpenKeyExW(HKEY_CURRENT_USER,
32                             REGSTR_PATH_SYSTRAY,
33                             0,
34                             KEY_READ,
35                             &hKey);
36     if (dwError != ERROR_SUCCESS)
37         goto done;
38 
39     dwSize = sizeof(dwFlags);
40     dwError = RegQueryValueExW(hKey,
41                                L"HotPlugFlags",
42                                NULL,
43                                NULL,
44                                (LPBYTE)&dwFlags,
45                                &dwSize);
46     if (dwError != ERROR_SUCCESS)
47         dwFlags = 0;
48 
49 done:
50     if (hKey != NULL)
51         RegCloseKey(hKey);
52 
53     return dwFlags;
54 }
55 
56 static
57 DWORD
58 SetHotPlugFlags(
59     _In_ DWORD dwFlags)
60 {
61     HKEY hKey = NULL;
62     DWORD dwError;
63 
64     dwError = RegCreateKeyExW(HKEY_CURRENT_USER,
65                               REGSTR_PATH_SYSTRAY,
66                               0,
67                               NULL,
68                               REG_OPTION_NON_VOLATILE,
69                               KEY_WRITE,
70                               NULL,
71                               &hKey,
72                               NULL);
73     if (dwError != ERROR_SUCCESS)
74         goto done;
75 
76     dwError = RegSetValueExW(hKey,
77                              L"HotPlugFlags",
78                              0,
79                              REG_DWORD,
80                              (LPBYTE)&dwFlags,
81                              sizeof(dwFlags));
82 
83 done:
84     if (hKey != NULL)
85         RegCloseKey(hKey);
86 
87     return dwError;
88 }
89 
90 static
91 VOID
92 UpdateDialog(
93     _In_ HWND hwndDlg)
94 {
95     HWND hwndDeviceTree;
96     BOOL bHasItem;
97 
98     hwndDeviceTree = GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE);
99 
100     bHasItem = (TreeView_GetCount(hwndDeviceTree) != 0);
101     if (bHasItem)
102         TreeView_SelectItem(hwndDeviceTree, TreeView_GetFirstVisible(hwndDeviceTree));
103 
104     EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_PROPERTIES), bHasItem);
105     EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_STOP), bHasItem);
106 }
107 
108 static
109 VOID
110 ShowContextMenu(
111     HWND hwndDlg,
112     HWND hwndTreeView,
113     PHOTPLUG_DATA pHotplugData)
114 {
115     HTREEITEM hTreeItem;
116     RECT rc;
117     POINT pt;
118 
119     hTreeItem = TreeView_GetSelection(hwndTreeView);
120     if (hTreeItem == NULL)
121         return;
122 
123     TreeView_GetItemRect(hwndTreeView, hTreeItem, &rc, TRUE);
124 
125     pt.x = (rc.left + rc.right) / 2;
126     pt.y = (rc.top + rc.bottom) / 2;
127     ClientToScreen(hwndTreeView, &pt);
128     TrackPopupMenu(GetSubMenu(pHotplugData->hPopupMenu, 0),
129                    TPM_LEFTALIGN | TPM_TOPALIGN,
130                    pt.x,
131                    pt.y,
132                    0,
133                    hwndDlg,
134                    NULL);
135 }
136 
137 static
138 DEVINST
139 GetSelectedDeviceInst(
140     _In_ PHOTPLUG_DATA pHotplugData)
141 {
142     HTREEITEM hTreeItem;
143     TVITEMW item;
144 
145     hTreeItem = TreeView_GetSelection(pHotplugData->hwndDeviceTree);
146     if (hTreeItem == NULL)
147         return 0;
148 
149     ZeroMemory(&item, sizeof(item));
150     item.mask = TVIF_PARAM;
151     item.hItem = hTreeItem;
152 
153     TreeView_GetItem(pHotplugData->hwndDeviceTree, &item);
154 
155     return item.lParam;
156 }
157 
158 static
159 VOID
160 ShowDeviceProperties(
161     _In_ HWND hwndParent,
162     _In_ DEVINST DevInst)
163 {
164     ULONG ulSize;
165     CONFIGRET cr;
166     LPWSTR pszDevId;
167 
168     cr = CM_Get_Device_ID_Size(&ulSize, DevInst, 0);
169     if (cr != CR_SUCCESS || ulSize == 0)
170         return;
171 
172     /* Take the terminating NULL into account */
173     ulSize++;
174 
175     pszDevId = HeapAlloc(GetProcessHeap(), 0, ulSize * sizeof(WCHAR));
176     if (pszDevId == NULL)
177         return;
178 
179     cr = CM_Get_Device_IDW(DevInst, pszDevId, ulSize, 0);
180     if (cr == CR_SUCCESS)
181     {
182         typedef int (WINAPI *PFDEVICEPROPERTIESW)(HWND, LPCWSTR, LPCWSTR, BOOL);
183         HMODULE hDevMgrDll;
184         PFDEVICEPROPERTIESW pDevicePropertiesW;
185 
186         hDevMgrDll = LoadLibraryW(L"devmgr.dll");
187         if (hDevMgrDll != NULL)
188         {
189             pDevicePropertiesW = (PFDEVICEPROPERTIESW)GetProcAddress(hDevMgrDll, "DevicePropertiesW");
190             if (pDevicePropertiesW != NULL)
191                 pDevicePropertiesW(hwndParent, NULL, pszDevId, FALSE);
192 
193             FreeLibrary(hDevMgrDll);
194         }
195     }
196 
197     HeapFree(GetProcessHeap(), 0, pszDevId);
198 }
199 
200 INT_PTR
201 CALLBACK
202 SafeRemovalDlgProc(
203     HWND hwndDlg,
204     UINT uMsg,
205     WPARAM wParam,
206     LPARAM lParam)
207 {
208     PHOTPLUG_DATA pHotplugData;
209 
210     pHotplugData = (PHOTPLUG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
211 
212     switch (uMsg)
213     {
214         case WM_INITDIALOG:
215             pHotplugData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HOTPLUG_DATA));
216             if (pHotplugData != NULL)
217             {
218                 WCHAR szWindowTitle[MAX_PATH];
219 
220                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pHotplugData);
221 
222                 if (LoadStringW(hApplet,
223                                 IDS_CPLNAME,
224                                 szWindowTitle,
225                                 ARRAYSIZE(szWindowTitle)))
226                 {
227                     SetWindowTextW(hwndDlg, szWindowTitle);
228                 }
229 
230                 pHotplugData->hIcon = (HICON)LoadImageW(hApplet,
231                                                         MAKEINTRESOURCEW(IDI_HOTPLUG),
232                                                         IMAGE_ICON,
233                                                         GetSystemMetrics(SM_CXICON),
234                                                         GetSystemMetrics(SM_CYICON),
235                                                         LR_DEFAULTCOLOR);
236                 pHotplugData->hIconSm = (HICON)LoadImageW(hApplet,
237                                                           MAKEINTRESOURCEW(IDI_HOTPLUG),
238                                                           IMAGE_ICON,
239                                                           GetSystemMetrics(SM_CXSMICON),
240                                                           GetSystemMetrics(SM_CYSMICON),
241                                                           LR_DEFAULTCOLOR);
242                 SendMessageW(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)pHotplugData->hIcon);
243                 SendMessageW(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)pHotplugData->hIconSm);
244 
245                 pHotplugData->ImageListData.cbSize = sizeof(pHotplugData->ImageListData);
246                 SetupDiGetClassImageList(&pHotplugData->ImageListData);
247 
248                 pHotplugData->hPopupMenu = LoadMenu(hApplet, MAKEINTRESOURCE(IDM_POPUP_DEVICE_TREE));
249                 pHotplugData->hwndDeviceTree = GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE);
250                 pHotplugData->dwFlags = GetHotPlugFlags();
251 
252                 if (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS)
253                     Button_SetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS), BST_CHECKED);
254 
255                 TreeView_SetImageList(pHotplugData->hwndDeviceTree,
256                                       pHotplugData->ImageListData.ImageList,
257                                       TVSIL_NORMAL);
258 
259                 EnumHotpluggedDevices(pHotplugData);
260                 UpdateDialog(hwndDlg);
261             }
262             return TRUE;
263 
264         case WM_COMMAND:
265             switch (LOWORD(wParam))
266             {
267                 case IDCLOSE:
268                     KillTimer(hwndDlg, 1);
269                     EndDialog(hwndDlg, TRUE);
270                     break;
271 
272                 case IDC_SAFE_REMOVE_DISPLAY_COMPONENTS:
273                     if (HIWORD(wParam) == BN_CLICKED)
274                     {
275                         if (pHotplugData != NULL)
276                         {
277                             if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS)) == BST_CHECKED)
278                                 pHotplugData->dwFlags |= HOTPLUG_DISPLAY_DEVICE_COMPONENTS;
279                             else
280                                 pHotplugData->dwFlags &= ~HOTPLUG_DISPLAY_DEVICE_COMPONENTS;
281 
282                             SetHotPlugFlags(pHotplugData->dwFlags);
283 
284                             EnumHotpluggedDevices(pHotplugData);
285                             UpdateDialog(hwndDlg);
286                         }
287                     }
288                     break;
289 
290                 case IDC_SAFE_REMOVE_PROPERTIES:
291                 case IDM_PROPERTIES:
292                     ShowDeviceProperties(hwndDlg, GetSelectedDeviceInst(pHotplugData));
293                     break;
294 
295                 case IDC_SAFE_REMOVE_STOP:
296                 case IDM_STOP:
297                 {
298                     if (pHotplugData != NULL)
299                     {
300                         DialogBoxParamW(hApplet,
301                                         MAKEINTRESOURCEW(IDD_CONFIRM_STOP_HARDWARE_DIALOG),
302                                         hwndDlg,
303                                         ConfirmRemovalDlgProc,
304                                         (LPARAM)pHotplugData);
305                     }
306 
307                     break;
308                 }
309             }
310             break;
311 
312         case WM_DEVICECHANGE:
313             switch (wParam)
314             {
315                 case DBT_DEVNODES_CHANGED:
316                     SetTimer(hwndDlg, 1, 500, NULL);
317                     break;
318             }
319             break;
320 
321         case WM_TIMER:
322             if (wParam == 1)
323             {
324                 KillTimer(hwndDlg, 1);
325 
326                 if (pHotplugData != NULL)
327                 {
328                     EnumHotpluggedDevices(pHotplugData);
329                     UpdateDialog(hwndDlg);
330                 }
331             }
332             break;
333 
334         case WM_NOTIFY:
335             if (((LPNMHDR)lParam)->idFrom == IDC_SAFE_REMOVE_DEVICE_TREE)
336             {
337                 if (((LPNMHDR)lParam)->code == NM_RCLICK)
338                 {
339                     if (pHotplugData != NULL)
340                     {
341                         ShowContextMenu(hwndDlg,
342                                         ((LPNMHDR)lParam)->hwndFrom,
343                                         pHotplugData);
344                         return TRUE;
345                     }
346                 }
347             }
348             break;
349 
350         case WM_CLOSE:
351             KillTimer(hwndDlg, 1);
352             EndDialog(hwndDlg, TRUE);
353             break;
354 
355         case WM_DESTROY:
356             if (pHotplugData != NULL)
357             {
358                 if (pHotplugData->hPopupMenu != NULL)
359                     DestroyMenu(pHotplugData->hPopupMenu);
360 
361                 SetupDiDestroyClassImageList(&pHotplugData->ImageListData);
362 
363                 if (pHotplugData->hIconSm)
364                     DestroyIcon(pHotplugData->hIconSm);
365 
366                 if (pHotplugData->hIcon)
367                     DestroyIcon(pHotplugData->hIcon);
368 
369                 HeapFree(GetProcessHeap(), 0, pHotplugData);
370                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
371             }
372             break;
373     }
374 
375     return FALSE;
376 }
377 
378 LONG
379 APIENTRY
380 InitApplet(
381     HWND hwnd,
382     UINT uMsg,
383     LPARAM wParam,
384     LPARAM lParam)
385 {
386     DPRINT("InitApplet()\n");
387 
388     DialogBox(hApplet,
389               MAKEINTRESOURCE(IDD_SAFE_REMOVE_HARDWARE_DIALOG),
390               hwnd,
391               SafeRemovalDlgProc);
392 
393     // TODO
394     return TRUE;
395 }
396 
397 LONG
398 CALLBACK
399 CPlApplet(
400     HWND hwndCPl,
401     UINT uMsg,
402     LPARAM lParam1,
403     LPARAM lParam2)
404 {
405     UINT i = (UINT)lParam1;
406 
407     switch(uMsg)
408     {
409         case CPL_INIT:
410             return TRUE;
411 
412         case CPL_GETCOUNT:
413             return NUM_APPLETS;
414 
415         case CPL_INQUIRE:
416             if (i < NUM_APPLETS)
417             {
418                 CPLINFO *CPlInfo = (CPLINFO*)lParam2;
419                 CPlInfo->lData = 0;
420                 CPlInfo->idIcon = Applets[i].idIcon;
421                 CPlInfo->idName = Applets[i].idName;
422                 CPlInfo->idInfo = Applets[i].idDescription;
423             }
424             else
425             {
426                 return TRUE;
427             }
428             break;
429 
430         case CPL_DBLCLK:
431             if (i < NUM_APPLETS)
432                 Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2);
433             else
434                 return TRUE;
435             break;
436 
437         case CPL_STARTWPARMSW:
438             if (i < NUM_APPLETS)
439                 return Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2);
440             break;
441     }
442     return FALSE;
443 }
444 
445 INT
446 WINAPI
447 DllMain(
448     HINSTANCE hinstDLL,
449     DWORD dwReason,
450     LPVOID lpvReserved)
451 {
452     UNREFERENCED_PARAMETER(lpvReserved);
453 
454     switch (dwReason)
455     {
456         case DLL_PROCESS_ATTACH:
457         case DLL_THREAD_ATTACH:
458             hApplet = hinstDLL;
459             break;
460     }
461     return TRUE;
462 }
463