|
1 | 1 | #pragma once |
2 | 2 | #include <windows.h> |
3 | | -#include <cfgmgr32.h> // for CM_Get_Device_Interface_List |
4 | 3 | #include <hidsdi.h> |
| 4 | +#include <SetupAPI.h> |
5 | 5 | #include <wrl/wrappers/corewrappers.h> |
6 | 6 |
|
7 | 7 | #include <cassert> |
8 | 8 | #include <string> |
9 | 9 | #include <vector> |
10 | 10 |
|
11 | 11 | #pragma comment(lib, "hid.lib") |
12 | | -#pragma comment(lib, "mincore.lib") |
| 12 | +#pragma comment (lib, "Setupapi.lib") |
13 | 13 |
|
14 | 14 |
|
15 | 15 | /** RAII wrapper of PHIDP_PREPARSED_DATA. */ |
@@ -63,28 +63,83 @@ class HID { |
63 | 63 | GUID hidguid = {}; |
64 | 64 | HidD_GetHidGuid(&hidguid); |
65 | 65 |
|
66 | | - const ULONG searchScope = CM_GET_DEVICE_INTERFACE_LIST_PRESENT; // only currently 'live' device interfaces |
67 | | - |
68 | | - ULONG deviceInterfaceListLength = 0; |
69 | | - CONFIGRET cr = CM_Get_Device_Interface_List_SizeW(&deviceInterfaceListLength, &hidguid, NULL, searchScope); |
70 | | - assert(cr == CR_SUCCESS); |
71 | | - |
72 | | - // symbolic link name of interface instances |
73 | | - std::wstring deviceInterfaceList(deviceInterfaceListLength, L'\0'); |
74 | | - cr = CM_Get_Device_Interface_ListW(&hidguid, NULL, const_cast<wchar_t*>(deviceInterfaceList.data()), deviceInterfaceListLength, searchScope); |
75 | | - assert(cr == CR_SUCCESS); |
| 66 | + // Retrieve a list of all present USB devices with a device interface. |
| 67 | + HDEVINFO devInfoSet = SetupDiGetClassDevsW(&hidguid, NULL, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
| 68 | + assert(devInfoSet != INVALID_HANDLE_VALUE); |
76 | 69 |
|
77 | 70 | std::vector<Match> results; |
78 | | - for (const wchar_t * currentInterface = deviceInterfaceList.c_str(); *currentInterface; currentInterface += wcslen(currentInterface) + 1) { |
79 | | - auto result = CheckDevice(currentInterface, query, verbose); |
| 71 | + for (DWORD devIdx = 0;; ++devIdx) { |
| 72 | + // get device information |
| 73 | + SP_DEVINFO_DATA devInfoData = {}; |
| 74 | + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); |
| 75 | + if (!SetupDiEnumDeviceInfo(devInfoSet, devIdx, &devInfoData)) |
| 76 | + break; |
| 77 | + |
| 78 | + // list of hardware IDs |
| 79 | + auto hw_id_list = GetHWIds(devInfoSet, &devInfoData); |
| 80 | + if (verbose) { |
| 81 | + for (const auto& hw_id : hw_id_list) |
| 82 | + printf(" HW ID: %ls\n", hw_id.c_str()); |
| 83 | + } |
| 84 | + |
| 85 | + // get device interfaces |
| 86 | + SP_DEVICE_INTERFACE_DATA devIfData = {}; |
| 87 | + devIfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
| 88 | + BOOL ok = SetupDiEnumDeviceInterfaces(devInfoSet, NULL, &hidguid, devIdx, &devIfData); |
| 89 | + assert(ok); |
| 90 | + |
| 91 | + std::vector<BYTE> devIfcDetailDataBuf; |
| 92 | + SP_DEVICE_INTERFACE_DETAIL_DATA_W* devIfcDetailData = nullptr; |
| 93 | + { |
| 94 | + DWORD size = 0; |
| 95 | + SetupDiGetDeviceInterfaceDetailW(devInfoSet, &devIfData, NULL, 0, &size, NULL); // expected to fail |
| 96 | + |
| 97 | + devIfcDetailDataBuf.resize(size, (BYTE)0); |
| 98 | + devIfcDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)devIfcDetailDataBuf.data(); |
| 99 | + devIfcDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); |
| 100 | + |
| 101 | + // get details about a device interface |
| 102 | + ok = SetupDiGetDeviceInterfaceDetailW(devInfoSet, &devIfData, devIfcDetailData, size, &size, NULL); |
| 103 | + assert(ok); |
| 104 | + } |
| 105 | + |
| 106 | + auto result = CheckDevice(devIfcDetailData->DevicePath, query, verbose); |
80 | 107 | if (!result.name.empty()) |
81 | 108 | results.push_back(std::move(result)); |
82 | 109 | } |
83 | 110 |
|
| 111 | + SetupDiDestroyDeviceInfoList(devInfoSet); |
| 112 | + |
84 | 113 | return results; |
85 | 114 | } |
86 | 115 |
|
87 | 116 | private: |
| 117 | + /** Get list of hardware IDs for a device. */ |
| 118 | + static std::vector<std::wstring> GetHWIds(HDEVINFO devInfoSet, SP_DEVINFO_DATA* devInfoData) { |
| 119 | + // Get required size for device property |
| 120 | + DWORD type = 0; |
| 121 | + DWORD size = 0; |
| 122 | + SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfoData, SPDRP_HARDWAREID, &type, NULL, 0, &size); // expected to fail |
| 123 | + |
| 124 | + // Get SPDRP_HARDWAREID device property in REG_MULTI_SZ string format |
| 125 | + std::vector<wchar_t> hw_ids(size/sizeof(wchar_t), L'\0'); |
| 126 | + BOOL ok = SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfoData, SPDRP_HARDWAREID, &type, (BYTE*)hw_ids.data(), size, NULL); |
| 127 | + assert(ok); |
| 128 | + |
| 129 | + return ParseMultiSz(hw_ids.data()); |
| 130 | + } |
| 131 | + |
| 132 | + /** Parse REG_MULTI_SZ string into a list of strings. */ |
| 133 | + static std::vector<std::wstring> ParseMultiSz(const wchar_t* ptr) { |
| 134 | + std::vector<std::wstring> result; |
| 135 | + // parse list of zero-terminated strings that are terminated by a null-pointer |
| 136 | + while (*ptr) { |
| 137 | + result.emplace_back(ptr); |
| 138 | + ptr += lstrlenW(ptr) + 1; // advance to next string |
| 139 | + } |
| 140 | + return result; |
| 141 | + } |
| 142 | + |
88 | 143 | static Match CheckDevice(const wchar_t* deviceName, const Query& query, bool verbose) { |
89 | 144 | FileHandle hid_dev(CreateFileW(deviceName, |
90 | 145 | GENERIC_READ | GENERIC_WRITE, |
|
0 commit comments