xref: /reactos/dll/cpl/hotplug/hotplug.c (revision 40462c92)
1 /*
2 * PROJECT:     Safely Remove Hardware Applet
3 * LICENSE:     GPL - See COPYING in the top level directory
4 * FILE:        dll/cpl/hotplug/hotplug.c
5 * PURPOSE:     applet initialization
6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
7 */
8 
9 #include "hotplug.h"
10 
11 #include <initguid.h>
12 #include <devguid.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 
18 typedef struct _HOTPLUG_DATA
19 {
20     SP_CLASSIMAGELIST_DATA ImageListData;
21     HMENU hPopupMenu;
22     DWORD dwFlags;
23 } HOTPLUG_DATA, *PHOTPLUG_DATA;
24 
25 
26 // globals
27 HINSTANCE hApplet = 0;
28 
29 /* Applets */
30 APPLET Applets[NUM_APPLETS] =
31 {
32     {IDI_HOTPLUG, IDS_CPLNAME, IDS_CPLDESCRIPTION, InitApplet}
33 };
34 
35 static
36 DWORD
37 GetHotPlugFlags(VOID)
38 {
39     HKEY hKey = NULL;
40     DWORD dwFlags = 0;
41     DWORD dwSize, dwError;
42 
43     dwError = RegOpenKeyExW(HKEY_CURRENT_USER,
44                             REGSTR_PATH_SYSTRAY,
45                             0,
46                             KEY_READ,
47                             &hKey);
48     if (dwError != ERROR_SUCCESS)
49         goto done;
50 
51     dwSize = sizeof(dwFlags);
52     dwError = RegQueryValueExW(hKey,
53                                L"HotPlugFlags",
54                                NULL,
55                                NULL,
56                                (LPBYTE)&dwFlags,
57                                &dwSize);
58     if (dwError != ERROR_SUCCESS)
59         dwFlags = 0;
60 
61 done:
62     if (hKey != NULL)
63         RegCloseKey(hKey);
64 
65     return dwFlags;
66 }
67 
68 
69 static
70 DWORD
71 SetHotPlugFlags(
72     _In_ DWORD dwFlags)
73 {
74     HKEY hKey = NULL;
75     DWORD dwError;
76 
77     dwError = RegCreateKeyExW(HKEY_CURRENT_USER,
78                               REGSTR_PATH_SYSTRAY,
79                               0,
80                               NULL,
81                               REG_OPTION_NON_VOLATILE,
82                               KEY_WRITE,
83                               NULL,
84                               &hKey,
85                               NULL);
86     if (dwError != ERROR_SUCCESS)
87         goto done;
88 
89     dwError = RegSetValueExW(hKey,
90                              L"HotPlugFlags",
91                              0,
92                              REG_DWORD,
93                              (LPBYTE)&dwFlags,
94                              sizeof(dwFlags));
95 
96 done:
97     if (hKey != NULL)
98         RegCloseKey(hKey);
99 
100     return dwError;
101 }
102 
103 
104 static
105 HTREEITEM
106 InsertDeviceTreeItem(
107     _In_ HWND hwndDeviceTree,
108     _In_ HTREEITEM hParent,
109     _In_ DWORD DevInst,
110     _In_ PHOTPLUG_DATA pHotplugData)
111 {
112     WCHAR szDisplayName[40];
113     WCHAR szGuidString[MAX_GUID_STRING_LEN];
114     TVINSERTSTRUCTW tvItem;
115     GUID ClassGuid;
116     INT nClassImage;
117     DWORD dwSize;
118     CONFIGRET cr;
119 
120     /* Get the device description */
121     dwSize = sizeof(szDisplayName);
122     cr = CM_Get_DevNode_Registry_Property(DevInst,
123                                           CM_DRP_DEVICEDESC,
124                                           NULL,
125                                           szDisplayName,
126                                           &dwSize,
127                                           0);
128     if (cr != CR_SUCCESS)
129         wcscpy(szDisplayName, L"Unknown Device");
130 
131     /* Get the class GUID */
132     dwSize = sizeof(szGuidString);
133     cr = CM_Get_DevNode_Registry_Property(DevInst,
134                                           CM_DRP_CLASSGUID,
135                                           NULL,
136                                           szGuidString,
137                                           &dwSize,
138                                           0);
139     if (cr == CR_SUCCESS)
140     {
141         pSetupGuidFromString(szGuidString, &ClassGuid);
142     }
143     else
144     {
145         memcpy(&ClassGuid, &GUID_DEVCLASS_UNKNOWN, sizeof(GUID));
146     }
147 
148     /* Get the image for the class this device is in */
149     SetupDiGetClassImageIndex(&pHotplugData->ImageListData,
150                               &ClassGuid,
151                               &nClassImage);
152 
153     /* Add it to the device tree */
154     ZeroMemory(&tvItem, sizeof(tvItem));
155     tvItem.hParent = hParent;
156     tvItem.hInsertAfter = TVI_LAST;
157 
158     tvItem.item.mask = TVIF_STATE | TVIF_TEXT /*| TVIF_PARAM*/ | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
159     tvItem.item.state = TVIS_EXPANDED;
160     tvItem.item.stateMask = TVIS_EXPANDED;
161     tvItem.item.pszText = szDisplayName;
162     tvItem.item.iImage = nClassImage;
163     tvItem.item.iSelectedImage = nClassImage;
164     tvItem.item.lParam = (LPARAM)NULL;
165 
166     return TreeView_InsertItem(hwndDeviceTree, &tvItem);
167 }
168 
169 
170 static
171 VOID
172 RecursiveInsertSubDevices(
173     _In_ HWND hwndDeviceTree,
174     _In_ HTREEITEM hParentItem,
175     _In_ DWORD ParentDevInst,
176     _In_ PHOTPLUG_DATA pHotplugData)
177 {
178     HTREEITEM hTreeItem;
179     DEVINST ChildDevInst;
180     CONFIGRET cr;
181 
182     DPRINT("RecursiveInsertSubDevices()\n");
183 
184     cr = CM_Get_Child(&ChildDevInst, ParentDevInst, 0);
185     if (cr != CR_SUCCESS)
186     {
187         DPRINT("No child! %lu\n", cr);
188         return;
189     }
190 
191     hTreeItem = InsertDeviceTreeItem(hwndDeviceTree,
192                                      hParentItem,
193                                      ChildDevInst,
194                                      pHotplugData);
195     if (hTreeItem != NULL)
196     {
197         RecursiveInsertSubDevices(hwndDeviceTree,
198                                   hTreeItem,
199                                   ChildDevInst,
200                                   pHotplugData);
201     }
202 
203     for (;;)
204     {
205         cr = CM_Get_Sibling(&ChildDevInst, ChildDevInst, 0);
206         if (cr != CR_SUCCESS)
207         {
208             DPRINT("No sibling! %lu\n", cr);
209             return;
210         }
211 
212         hTreeItem = InsertDeviceTreeItem(hwndDeviceTree,
213                                          hParentItem,
214                                          ChildDevInst,
215                                          pHotplugData);
216         if (hTreeItem != NULL)
217         {
218             RecursiveInsertSubDevices(hwndDeviceTree,
219                                       hTreeItem,
220                                       ChildDevInst,
221                                       pHotplugData);
222         }
223     }
224 }
225 
226 
227 static
228 VOID
229 EnumHotpluggedDevices(
230     HWND hwndDeviceTree,
231     PHOTPLUG_DATA pHotplugData)
232 {
233     SP_DEVINFO_DATA did = { 0 };
234     HDEVINFO hdev;
235     int idev;
236     DWORD dwCapabilities, dwSize;
237     ULONG ulStatus, ulProblem;
238     HTREEITEM hTreeItem;
239     CONFIGRET cr;
240 
241     DPRINT1("EnumHotpluggedDevices()\n");
242 
243     TreeView_DeleteAllItems(hwndDeviceTree);
244 
245     hdev = SetupDiGetClassDevs(NULL, NULL, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT);
246     if (hdev == INVALID_HANDLE_VALUE)
247         return;
248 
249     did.cbSize = sizeof(did);
250 
251     /* Enumerate all the attached devices */
252     for (idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); idev++)
253     {
254         ulStatus = 0;
255         ulProblem = 0;
256 
257         cr = CM_Get_DevNode_Status(&ulStatus,
258                                    &ulProblem,
259                                    did.DevInst,
260                                    0);
261         if (cr != CR_SUCCESS)
262             continue;
263 
264         dwCapabilities = 0,
265         dwSize = sizeof(dwCapabilities);
266         cr = CM_Get_DevNode_Registry_Property(did.DevInst,
267                                               CM_DRP_CAPABILITIES,
268                                               NULL,
269                                               &dwCapabilities,
270                                               &dwSize,
271                                               0);
272         if (cr != CR_SUCCESS)
273             continue;
274 
275         /* Add devices that require safe removal to the device tree */
276         if ( (dwCapabilities & CM_DEVCAP_REMOVABLE) &&
277             !(dwCapabilities & CM_DEVCAP_DOCKDEVICE) &&
278             !(dwCapabilities & CM_DEVCAP_SURPRISEREMOVALOK) &&
279             ((dwCapabilities & CM_DEVCAP_EJECTSUPPORTED) || (ulStatus & DN_DISABLEABLE)) &&
280             ulProblem == 0)
281         {
282             hTreeItem = InsertDeviceTreeItem(hwndDeviceTree,
283                                              TVI_ROOT,
284                                              did.DevInst,
285                                              pHotplugData);
286 
287             if ((hTreeItem != NULL) && (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS))
288             {
289                 RecursiveInsertSubDevices(hwndDeviceTree,
290                                           hTreeItem,
291                                           did.DevInst,
292                                           pHotplugData);
293             }
294         }
295     }
296 
297     SetupDiDestroyDeviceInfoList(hdev);
298 }
299 
300 
301 static
302 VOID
303 UpdateButtons(
304     HWND hwndDlg)
305 {
306     BOOL bEnabled;
307 
308     bEnabled = (TreeView_GetCount(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE)) != 0);
309 
310     EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_PROPERTIES), bEnabled);
311     EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_STOP), bEnabled);
312 }
313 
314 
315 static
316 VOID
317 ShowContextMenu(
318     HWND hwndDlg,
319     HWND hwndTreeView,
320     PHOTPLUG_DATA pHotplugData)
321 {
322     HTREEITEM hTreeItem;
323     RECT rc;
324     POINT pt;
325 
326     hTreeItem = TreeView_GetSelection(hwndTreeView);
327     if (hTreeItem == NULL)
328         return;
329 
330     TreeView_GetItemRect(hwndTreeView, hTreeItem, &rc, TRUE);
331 
332     pt.x = (rc.left + rc.right) / 2;
333     pt.y = (rc.top + rc.bottom) / 2;
334     ClientToScreen(hwndTreeView, &pt);
335     TrackPopupMenu(GetSubMenu(pHotplugData->hPopupMenu, 0),
336                    TPM_LEFTALIGN | TPM_TOPALIGN,
337                    pt.x,
338                    pt.y,
339                    0,
340                    hwndDlg,
341                    NULL);
342 }
343 
344 
345 INT_PTR
346 CALLBACK
347 SafeRemovalDlgProc(
348     HWND hwndDlg,
349     UINT uMsg,
350     WPARAM wParam,
351     LPARAM lParam)
352 {
353     PHOTPLUG_DATA pHotplugData;
354 
355     pHotplugData = (PHOTPLUG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
356 
357     switch (uMsg)
358     {
359         case WM_INITDIALOG:
360             pHotplugData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HOTPLUG_DATA));
361             if (pHotplugData != NULL)
362             {
363                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pHotplugData);
364 
365                 pHotplugData->ImageListData.cbSize = sizeof(pHotplugData->ImageListData);
366                 SetupDiGetClassImageList(&pHotplugData->ImageListData);
367 
368                 pHotplugData->hPopupMenu = LoadMenu(hApplet, MAKEINTRESOURCE(IDM_POPUP_DEVICE_TREE));
369 
370                 pHotplugData->dwFlags = GetHotPlugFlags();
371 
372                 if (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS)
373                     Button_SetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS), BST_CHECKED);
374 
375                 TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE),
376                                       pHotplugData->ImageListData.ImageList,
377                                       TVSIL_NORMAL);
378 
379                 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE),
380                                       pHotplugData);
381                 UpdateButtons(hwndDlg);
382             }
383             return TRUE;
384 
385         case WM_COMMAND:
386             switch (LOWORD(wParam))
387             {
388                 case IDCLOSE:
389                     KillTimer(hwndDlg, 1);
390                     EndDialog(hwndDlg, TRUE);
391                     break;
392 
393                 case IDC_SAFE_REMOVE_DISPLAY_COMPONENTS:
394                     if (HIWORD(wParam) == BN_CLICKED)
395                     {
396                         if (pHotplugData != NULL)
397                         {
398                             if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS)) == BST_CHECKED)
399                                 pHotplugData->dwFlags |= HOTPLUG_DISPLAY_DEVICE_COMPONENTS;
400                             else
401                                 pHotplugData->dwFlags &= ~HOTPLUG_DISPLAY_DEVICE_COMPONENTS;
402 
403                             SetHotPlugFlags(pHotplugData->dwFlags);
404 
405                             EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE),
406                                                   pHotplugData);
407                         }
408                     }
409                     break;
410             }
411             break;
412 
413         case WM_DEVICECHANGE:
414             switch (wParam)
415             {
416                 case DBT_DEVNODES_CHANGED:
417                     SetTimer(hwndDlg, 1, 500, NULL);
418                     break;
419             }
420             break;
421 
422         case WM_TIMER:
423             if (wParam == 1)
424             {
425                 KillTimer(hwndDlg, 1);
426 
427                 if (pHotplugData != NULL)
428                 {
429                     EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE),
430                                           pHotplugData);
431                     UpdateButtons(hwndDlg);
432                 }
433             }
434             break;
435 
436         case WM_NOTIFY:
437             if (((LPNMHDR)lParam)->idFrom == IDC_SAFE_REMOVE_DEVICE_TREE)
438             {
439                 if (((LPNMHDR)lParam)->code == NM_RCLICK)
440                 {
441                     if (pHotplugData != NULL)
442                     {
443                         ShowContextMenu(hwndDlg,
444                                         ((LPNMHDR)lParam)->hwndFrom,
445                                         pHotplugData);
446                         return TRUE;
447                     }
448                 }
449             }
450             break;
451 
452         case WM_CLOSE:
453             KillTimer(hwndDlg, 1);
454             EndDialog(hwndDlg, TRUE);
455             break;
456 
457         case WM_DESTROY:
458             if (pHotplugData != NULL)
459             {
460                 if (pHotplugData->hPopupMenu != NULL)
461                     DestroyMenu(pHotplugData->hPopupMenu);
462 
463                 SetupDiDestroyClassImageList(&pHotplugData->ImageListData);
464 
465                 HeapFree(GetProcessHeap(), 0, pHotplugData);
466                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
467             }
468             break;
469     }
470 
471     return FALSE;
472 }
473 
474 
475 LONG
476 APIENTRY
477 InitApplet(
478     HWND hwnd,
479     UINT uMsg,
480     LPARAM wParam,
481     LPARAM lParam)
482 {
483     DPRINT("InitApplet()\n");
484 
485     DialogBox(hApplet,
486               MAKEINTRESOURCE(IDD_SAFE_REMOVE_HARDWARE_DIALOG),
487               hwnd,
488               SafeRemovalDlgProc);
489 
490     // TODO
491     return TRUE;
492 }
493 
494 
495 LONG
496 CALLBACK
497 CPlApplet(
498     HWND hwndCPl,
499     UINT uMsg,
500     LPARAM lParam1,
501     LPARAM lParam2)
502 {
503     UINT i = (UINT)lParam1;
504 
505     switch(uMsg)
506     {
507         case CPL_INIT:
508             return TRUE;
509 
510         case CPL_GETCOUNT:
511             return NUM_APPLETS;
512 
513         case CPL_INQUIRE:
514             {
515                 CPLINFO *CPlInfo = (CPLINFO*)lParam2;
516                 CPlInfo->lData = 0;
517                 CPlInfo->idIcon = Applets[i].idIcon;
518                 CPlInfo->idName = Applets[i].idName;
519                 CPlInfo->idInfo = Applets[i].idDescription;
520             }
521             break;
522 
523         case CPL_DBLCLK:
524             Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2);
525             break;
526 
527         case CPL_STARTWPARMSW:
528             return Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2);
529     }
530     return FALSE;
531 }
532 
533 
534 INT
535 WINAPI
536 DllMain(
537     HINSTANCE hinstDLL,
538     DWORD dwReason,
539     LPVOID lpvReserved)
540 {
541     UNREFERENCED_PARAMETER(lpvReserved);
542 
543     switch (dwReason)
544     {
545         case DLL_PROCESS_ATTACH:
546         case DLL_THREAD_ATTACH:
547             hApplet = hinstDLL;
548             break;
549     }
550     return TRUE;
551 }
552