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 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 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 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 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 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 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 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