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