xref: /reactos/dll/cpl/hotplug/enum.c (revision 81ba804b)
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:     Device enumeration
5  * COPYRIGHT:   Copyright 2020 Eric Kohl <eric.kohl@reactos.org>
6  *              Copyright 2023 Thamatip Chitpong <thamatip.chitpong@reactos.org>
7  */
8 
9 #include "hotplug.h"
10 
11 #include <initguid.h>
12 #include <devguid.h>
13 
14 #define MAX_DEVICE_DISPLAYNAME_LEN 256
15 
16 static
17 VOID
GetDeviceDisplayInfo(_In_ DEVINST DevInst,_In_ PHOTPLUG_DATA pHotplugData,_Out_writes_z_ (cchDesc)LPWSTR pszDesc,_In_ ULONG cchDesc,_Out_ PINT pImageIndex)18 GetDeviceDisplayInfo(
19     _In_ DEVINST DevInst,
20     _In_ PHOTPLUG_DATA pHotplugData,
21     _Out_writes_z_(cchDesc) LPWSTR pszDesc,
22     _In_ ULONG cchDesc,
23     _Out_ PINT pImageIndex)
24 {
25     WCHAR szGuidString[MAX_GUID_STRING_LEN];
26     GUID ClassGuid;
27     ULONG ulSize;
28     CONFIGRET cr;
29 
30     /* Get the device description */
31     ulSize = cchDesc * sizeof(WCHAR);
32     cr = CM_Get_DevNode_Registry_PropertyW(DevInst,
33                                            CM_DRP_FRIENDLYNAME,
34                                            NULL,
35                                            pszDesc,
36                                            &ulSize,
37                                            0);
38     if (cr != CR_SUCCESS)
39     {
40         ulSize = cchDesc * sizeof(WCHAR);
41         cr = CM_Get_DevNode_Registry_PropertyW(DevInst,
42                                                CM_DRP_DEVICEDESC,
43                                                NULL,
44                                                pszDesc,
45                                                &ulSize,
46                                                0);
47         if (cr != CR_SUCCESS)
48             LoadStringW(hApplet, IDS_UNKNOWN_DEVICE, pszDesc, cchDesc);
49     }
50 
51     /* Get the class GUID */
52     ulSize = sizeof(szGuidString);
53     cr = CM_Get_DevNode_Registry_PropertyW(DevInst,
54                                            CM_DRP_CLASSGUID,
55                                            NULL,
56                                            szGuidString,
57                                            &ulSize,
58                                            0);
59     if (cr == CR_SUCCESS)
60     {
61         pSetupGuidFromString(szGuidString, &ClassGuid);
62     }
63     else
64     {
65         ClassGuid = GUID_DEVCLASS_UNKNOWN;
66     }
67 
68     /* Get the image for the class this device is in */
69     SetupDiGetClassImageIndex(&pHotplugData->ImageListData,
70                               &ClassGuid,
71                               pImageIndex);
72 }
73 
74 static
75 HTREEITEM
InsertDeviceTreeItem(_In_ HTREEITEM hParent,_In_ DEVINST DevInst,_In_ PHOTPLUG_DATA pHotplugData)76 InsertDeviceTreeItem(
77     _In_ HTREEITEM hParent,
78     _In_ DEVINST DevInst,
79     _In_ PHOTPLUG_DATA pHotplugData)
80 {
81     WCHAR szDisplayName[MAX_DEVICE_DISPLAYNAME_LEN];
82     INT nClassImage;
83     TVINSERTSTRUCTW tvItem;
84 
85     GetDeviceDisplayInfo(DevInst,
86                          pHotplugData,
87                          szDisplayName,
88                          ARRAYSIZE(szDisplayName),
89                          &nClassImage);
90 
91     ZeroMemory(&tvItem, sizeof(tvItem));
92     tvItem.hParent = hParent;
93     tvItem.hInsertAfter = TVI_LAST;
94 
95     tvItem.item.mask = TVIF_STATE | TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
96     tvItem.item.state = TVIS_EXPANDED;
97     tvItem.item.stateMask = TVIS_EXPANDED;
98     tvItem.item.pszText = szDisplayName;
99     tvItem.item.iImage = nClassImage;
100     tvItem.item.iSelectedImage = nClassImage;
101     tvItem.item.lParam = (LPARAM)DevInst;
102 
103     return TreeView_InsertItem(pHotplugData->hwndDeviceTree, &tvItem);
104 }
105 
106 static
107 VOID
DevTreeRecursiveInsertSubDevices(_In_ HTREEITEM hParentItem,_In_ DEVINST ParentDevInst,_In_ PHOTPLUG_DATA pHotplugData)108 DevTreeRecursiveInsertSubDevices(
109     _In_ HTREEITEM hParentItem,
110     _In_ DEVINST ParentDevInst,
111     _In_ PHOTPLUG_DATA pHotplugData)
112 {
113     HTREEITEM hTreeItem;
114     DEVINST ChildDevInst;
115     CONFIGRET cr;
116 
117     cr = CM_Get_Child(&ChildDevInst, ParentDevInst, 0);
118     if (cr != CR_SUCCESS)
119         return;
120 
121     hTreeItem = InsertDeviceTreeItem(hParentItem,
122                                      ChildDevInst,
123                                      pHotplugData);
124     if (hTreeItem != NULL)
125     {
126         DevTreeRecursiveInsertSubDevices(hTreeItem,
127                                          ChildDevInst,
128                                          pHotplugData);
129     }
130 
131     for (;;)
132     {
133         cr = CM_Get_Sibling(&ChildDevInst, ChildDevInst, 0);
134         if (cr != CR_SUCCESS)
135             return;
136 
137         hTreeItem = InsertDeviceTreeItem(hParentItem,
138                                          ChildDevInst,
139                                          pHotplugData);
140         if (hTreeItem != NULL)
141         {
142             DevTreeRecursiveInsertSubDevices(hTreeItem,
143                                              ChildDevInst,
144                                              pHotplugData);
145         }
146     }
147 }
148 
149 VOID
EnumHotpluggedDevices(_In_ PHOTPLUG_DATA pHotplugData)150 EnumHotpluggedDevices(
151     _In_ PHOTPLUG_DATA pHotplugData)
152 {
153     SP_DEVINFO_DATA did = { 0 };
154     HDEVINFO hdev;
155     int idev;
156     DWORD dwCapabilities, dwSize;
157     ULONG ulStatus, ulProblem;
158     HTREEITEM hTreeItem;
159     CONFIGRET cr;
160 
161     TreeView_DeleteAllItems(pHotplugData->hwndDeviceTree);
162 
163     hdev = SetupDiGetClassDevs(NULL, NULL, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT);
164     if (hdev == INVALID_HANDLE_VALUE)
165         return;
166 
167     did.cbSize = sizeof(did);
168 
169     /* Enumerate all the attached devices */
170     for (idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); idev++)
171     {
172         ulStatus = 0;
173         ulProblem = 0;
174 
175         cr = CM_Get_DevNode_Status(&ulStatus,
176                                    &ulProblem,
177                                    did.DevInst,
178                                    0);
179         if (cr != CR_SUCCESS)
180             continue;
181 
182         dwCapabilities = 0,
183         dwSize = sizeof(dwCapabilities);
184         cr = CM_Get_DevNode_Registry_Property(did.DevInst,
185                                               CM_DRP_CAPABILITIES,
186                                               NULL,
187                                               &dwCapabilities,
188                                               &dwSize,
189                                               0);
190         if (cr != CR_SUCCESS)
191             continue;
192 
193         /* Add devices that require safe removal to the device tree */
194         if ( (dwCapabilities & CM_DEVCAP_REMOVABLE) &&
195             !(dwCapabilities & CM_DEVCAP_DOCKDEVICE) &&
196             !(dwCapabilities & CM_DEVCAP_SURPRISEREMOVALOK) &&
197             ((dwCapabilities & CM_DEVCAP_EJECTSUPPORTED) || (ulStatus & DN_DISABLEABLE)) &&
198             ulProblem == 0)
199         {
200             hTreeItem = InsertDeviceTreeItem(TVI_ROOT,
201                                              did.DevInst,
202                                              pHotplugData);
203 
204             if ((hTreeItem != NULL) && (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS))
205             {
206                 DevTreeRecursiveInsertSubDevices(hTreeItem,
207                                                  did.DevInst,
208                                                  pHotplugData);
209             }
210         }
211     }
212 
213     SetupDiDestroyDeviceInfoList(hdev);
214 }
215 
216 static
217 VOID
InsertConfirmDeviceListItem(_In_ HWND hwndCfmDeviceList,_In_ DEVINST DevInst,_In_ PHOTPLUG_DATA pHotplugData)218 InsertConfirmDeviceListItem(
219     _In_ HWND hwndCfmDeviceList,
220     _In_ DEVINST DevInst,
221     _In_ PHOTPLUG_DATA pHotplugData)
222 {
223     WCHAR szDisplayName[MAX_DEVICE_DISPLAYNAME_LEN];
224     INT nClassImage;
225     LVITEMW lvItem;
226 
227     GetDeviceDisplayInfo(DevInst,
228                          pHotplugData,
229                          szDisplayName,
230                          ARRAYSIZE(szDisplayName),
231                          &nClassImage);
232 
233     ZeroMemory(&lvItem, sizeof(lvItem));
234     lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
235     lvItem.iItem = ListView_GetItemCount(hwndCfmDeviceList);
236     lvItem.pszText = szDisplayName;
237     lvItem.iImage = nClassImage;
238     lvItem.lParam = (LPARAM)DevInst;
239 
240     ListView_InsertItem(hwndCfmDeviceList, &lvItem);
241 }
242 
243 static
244 VOID
CfmListRecursiveInsertSubDevices(_In_ HWND hwndCfmDeviceList,_In_ DEVINST ParentDevInst,_In_ PHOTPLUG_DATA pHotplugData)245 CfmListRecursiveInsertSubDevices(
246     _In_ HWND hwndCfmDeviceList,
247     _In_ DEVINST ParentDevInst,
248     _In_ PHOTPLUG_DATA pHotplugData)
249 {
250     DEVINST ChildDevInst;
251     CONFIGRET cr;
252 
253     cr = CM_Get_Child(&ChildDevInst, ParentDevInst, 0);
254     if (cr != CR_SUCCESS)
255         return;
256 
257     InsertConfirmDeviceListItem(hwndCfmDeviceList, ChildDevInst, pHotplugData);
258     CfmListRecursiveInsertSubDevices(hwndCfmDeviceList, ChildDevInst, pHotplugData);
259 
260     for (;;)
261     {
262         cr = CM_Get_Sibling(&ChildDevInst, ChildDevInst, 0);
263         if (cr != CR_SUCCESS)
264             return;
265 
266         InsertConfirmDeviceListItem(hwndCfmDeviceList, ChildDevInst, pHotplugData);
267         CfmListRecursiveInsertSubDevices(hwndCfmDeviceList, ChildDevInst, pHotplugData);
268     }
269 }
270 
271 VOID
CfmListEnumDevices(_In_ HWND hwndCfmDeviceList,_In_ PHOTPLUG_DATA pHotplugData)272 CfmListEnumDevices(
273     _In_ HWND hwndCfmDeviceList,
274     _In_ PHOTPLUG_DATA pHotplugData)
275 {
276     DEVINST DevInst;
277 
278     DevInst = GetDeviceInstForRemoval(pHotplugData);
279     if (DevInst != 0)
280     {
281         InsertConfirmDeviceListItem(hwndCfmDeviceList, DevInst, pHotplugData);
282         CfmListRecursiveInsertSubDevices(hwndCfmDeviceList, DevInst, pHotplugData);
283     }
284 }
285