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