Skip to content

Commit 923311f

Browse files
committed
Windows: reference code closer to Java
1 parent 2285e66 commit 923311f

File tree

10 files changed

+595
-492
lines changed

10 files changed

+595
-492
lines changed

reference/windows/USB/USB.vcxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
<ClCompile Include="assertion.cpp" />
2323
<ClCompile Include="configuration.cpp" />
2424
<ClCompile Include="config_parser.cpp" />
25+
<ClCompile Include="device_info_set.cpp" />
2526
<ClCompile Include="main.cpp" />
2627
<ClCompile Include="prng.cpp" />
2728
<ClCompile Include="speed_test.cpp" />
2829
<ClCompile Include="tests.cpp" />
2930
<ClCompile Include="usb_device.cpp" />
30-
<ClCompile Include="usb_device_info.cpp" />
3131
<ClCompile Include="usb_error.cpp" />
3232
<ClCompile Include="usb_iostream.cpp" />
3333
<ClCompile Include="usb_registry.cpp" />
@@ -37,12 +37,12 @@
3737
<ClInclude Include="blocking_queue.hpp" />
3838
<ClInclude Include="configuration.hpp" />
3939
<ClInclude Include="config_parser.hpp" />
40+
<ClInclude Include="device_info_set.h" />
4041
<ClInclude Include="prng.hpp" />
4142
<ClInclude Include="scope.hpp" />
4243
<ClInclude Include="speed_test.hpp" />
4344
<ClInclude Include="tests.hpp" />
4445
<ClInclude Include="usb_device.hpp" />
45-
<ClInclude Include="usb_device_info.hpp" />
4646
<ClInclude Include="usb_error.hpp" />
4747
<ClInclude Include="usb_iostream.hpp" />
4848
<ClInclude Include="usb_registry.hpp" />

reference/windows/USB/USB.vcxproj.filters

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@
3333
<ClCompile Include="usb_registry.cpp">
3434
<Filter>Source Files</Filter>
3535
</ClCompile>
36-
<ClCompile Include="usb_device_info.cpp">
37-
<Filter>Source Files</Filter>
38-
</ClCompile>
3936
<ClCompile Include="config_parser.cpp">
4037
<Filter>Source Files</Filter>
4138
</ClCompile>
@@ -51,6 +48,9 @@
5148
<ClCompile Include="usb_iostream.cpp">
5249
<Filter>Source Files</Filter>
5350
</ClCompile>
51+
<ClCompile Include="device_info_set.cpp">
52+
<Filter>Source Files</Filter>
53+
</ClCompile>
5454
</ItemGroup>
5555
<ItemGroup>
5656
<ClInclude Include="assertion.hpp">
@@ -71,9 +71,6 @@
7171
<ClInclude Include="usb_registry.hpp">
7272
<Filter>Header Files</Filter>
7373
</ClInclude>
74-
<ClInclude Include="usb_device_info.hpp">
75-
<Filter>Header Files</Filter>
76-
</ClInclude>
7774
<ClInclude Include="config_parser.hpp">
7875
<Filter>Header Files</Filter>
7976
</ClInclude>
@@ -92,5 +89,8 @@
9289
<ClInclude Include="blocking_queue.hpp">
9390
<Filter>Header Files</Filter>
9491
</ClInclude>
92+
<ClInclude Include="device_info_set.h">
93+
<Filter>Header Files</Filter>
94+
</ClInclude>
9595
</ItemGroup>
9696
</Project>
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
//
2+
// Java Does USB
3+
// Copyright (c) 2023 Manuel Bleichenbacher
4+
// Licensed under MIT License
5+
// https://opensource.org/licenses/MIT
6+
//
7+
// Reference C++ code for Windows
8+
//
9+
10+
#include "device_info_set.h"
11+
#include "usb_error.hpp"
12+
#include "scope.hpp"
13+
#include <devpkey.h>
14+
15+
device_info_set::device_info_set(HDEVINFO dev_info_set)
16+
: dev_info_set_(dev_info_set), dev_info_data_({ sizeof(dev_intf_data_) }),
17+
has_dev_intf_data_(false), dev_intf_data_({ sizeof(dev_intf_data_) }), iteration_index(-1)
18+
{
19+
}
20+
21+
device_info_set device_info_set::of_present_devices(const GUID& interface_guid, const std::wstring& instance_id) {
22+
auto dev_info_set = SetupDiGetClassDevsW(&interface_guid, !instance_id.empty() ? instance_id.c_str() : nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
23+
if (dev_info_set == INVALID_HANDLE_VALUE)
24+
usb_error::throw_error("internal error (SetupDiGetClassDevsW)");
25+
return device_info_set(dev_info_set);
26+
}
27+
28+
device_info_set device_info_set::of_instance(const std::wstring& instance_id) {
29+
auto instance = of_empty();
30+
instance.add_instance(instance_id);
31+
return instance;
32+
}
33+
34+
device_info_set device_info_set::of_path(const std::wstring& device_path) {
35+
auto instance = of_empty();
36+
instance.add_device_path(device_path);
37+
return instance;
38+
}
39+
40+
device_info_set device_info_set::of_empty() {
41+
auto dev_info_set = SetupDiCreateDeviceInfoList(nullptr, nullptr);
42+
if (dev_info_set == INVALID_HANDLE_VALUE)
43+
usb_error::throw_error("internal error (SetupDiCreateDeviceInfoList)");
44+
return device_info_set(dev_info_set);
45+
}
46+
47+
device_info_set::device_info_set(device_info_set&& info_set) noexcept
48+
: dev_info_set_(info_set.dev_info_set_), dev_info_data_(info_set.dev_info_data_),
49+
has_dev_intf_data_(info_set.has_dev_intf_data_), dev_intf_data_(info_set.dev_intf_data_),
50+
iteration_index(info_set.iteration_index) {
51+
info_set.dev_info_set_ = INVALID_HANDLE_VALUE;
52+
info_set.has_dev_intf_data_ = false;
53+
}
54+
55+
device_info_set::~device_info_set() {
56+
if (dev_info_set_ == INVALID_HANDLE_VALUE)
57+
return;
58+
59+
if (has_dev_intf_data_)
60+
SetupDiDeleteDeviceInterfaceData(dev_info_set_, &dev_intf_data_);
61+
SetupDiDestroyDeviceInfoList(dev_info_set_);
62+
}
63+
64+
void device_info_set::add_instance(const std::wstring& instance_id) {
65+
if (SetupDiOpenDeviceInfoW(dev_info_set_, instance_id.c_str(), nullptr, 0, &dev_info_data_) == 0)
66+
throw usb_error("internal error (SetupDiOpenDeviceInfoW)", GetLastError());
67+
}
68+
69+
void device_info_set::add_device_path(const std::wstring& device_path) {
70+
if (has_dev_intf_data_)
71+
throw usb_error("calling add_device_path() multiple times is not implemented");
72+
73+
// load device information into dev info set
74+
if (SetupDiOpenDeviceInterfaceW(dev_info_set_, device_path.c_str(), 0, &dev_intf_data_) == 0)
75+
usb_error::throw_error("internal error (SetupDiOpenDeviceInterfaceW)");
76+
has_dev_intf_data_ = true;
77+
78+
if (SetupDiGetDeviceInterfaceDetailW(dev_info_set_, &dev_intf_data_, nullptr, 0, nullptr, &dev_info_data_) == 0) {
79+
auto err = GetLastError();
80+
if (err != ERROR_INSUFFICIENT_BUFFER)
81+
throw usb_error("internal error (SetupDiGetDeviceInterfaceDetailW)", err);
82+
}
83+
}
84+
85+
bool device_info_set::next() {
86+
iteration_index += 1;
87+
88+
if (SetupDiEnumDeviceInfo(dev_info_set_, iteration_index, &dev_info_data_) == 0) {
89+
auto err = GetLastError();
90+
if (err == ERROR_NO_MORE_ITEMS)
91+
return false;
92+
throw usb_error("internal error (SetupDiEnumDeviceInfo)", err);
93+
}
94+
95+
return true;
96+
}
97+
98+
99+
uint32_t device_info_set::get_device_property_int(const DEVPROPKEY& prop_key) {
100+
// query property value
101+
DEVPROPTYPE property_type;
102+
uint32_t property_value = -1;
103+
if (!SetupDiGetDevicePropertyW(dev_info_set_, &dev_info_data_, &prop_key, &property_type, reinterpret_cast<PBYTE>(&property_value), sizeof(property_value), nullptr, 0))
104+
usb_error::throw_error("internal error (SetupDiGetDevicePropertyW)");
105+
106+
// check property type
107+
if (property_type != DEVPROP_TYPE_UINT32)
108+
throw usb_error("internal error (SetupDiGetDevicePropertyW)");
109+
110+
return property_value;
111+
}
112+
113+
std::vector<uint8_t> device_info_set::get_device_property_variable_length(const DEVPROPKEY& prop_key, DEVPROPTYPE expected_type) {
114+
115+
// query length
116+
DWORD required_size = 0;
117+
DEVPROPTYPE property_type;
118+
if (!SetupDiGetDevicePropertyW(dev_info_set_, &dev_info_data_, &prop_key, &property_type, nullptr, 0, &required_size, 0)) {
119+
DWORD err = GetLastError();
120+
if (err == ERROR_NOT_FOUND)
121+
return {};
122+
if (err != ERROR_INSUFFICIENT_BUFFER)
123+
throw usb_error("internal error (SetupDiGetDevicePropertyW)", err);
124+
}
125+
126+
// check property type
127+
if (property_type != expected_type)
128+
throw usb_error("internal error (SetupDiGetDevicePropertyW)");
129+
130+
// query property value
131+
std::vector<uint8_t> property_value;
132+
property_value.resize(required_size);
133+
if (!SetupDiGetDevicePropertyW(dev_info_set_, &dev_info_data_, &prop_key, &property_type, &property_value[0], required_size, nullptr, 0))
134+
usb_error::throw_error("internal error (SetupDiGetDevicePropertyW)");
135+
136+
return property_value;
137+
}
138+
139+
std::wstring device_info_set::get_device_property_string(const DEVPROPKEY& prop_key) {
140+
141+
auto property_value = get_device_property_variable_length(prop_key, DEVPROP_TYPE_STRING);
142+
if (property_value.size() == 0)
143+
return L"";
144+
return std::wstring(reinterpret_cast<const WCHAR*>(&property_value[0]));
145+
}
146+
147+
std::vector<std::wstring> device_info_set::get_device_property_string_list(const DEVPROPKEY& prop_key) {
148+
auto property_value = get_device_property_variable_length(prop_key, DEVPROP_TYPE_STRING | DEVPROP_TYPEMOD_LIST);
149+
if (property_value.size() == 0)
150+
return {};
151+
return split_string_list(reinterpret_cast<const WCHAR*>(&property_value[0]));
152+
}
153+
154+
std::vector<std::wstring> device_info_set::split_string_list(const wchar_t* str_list_raw) {
155+
std::vector<std::wstring> str_list;
156+
int offset = 0;
157+
while (str_list_raw[offset] != L'\0') {
158+
str_list.push_back(str_list_raw + offset);
159+
offset += static_cast<int>(str_list.back().length()) + 1;
160+
}
161+
162+
return str_list;
163+
}
164+
165+
bool device_info_set::is_composite_device() {
166+
std::wstring device_service = get_device_property_string(DEVPKEY_Device_Service);
167+
return lstrcmpiW(device_service.c_str(), L"usbccgp") == 0;
168+
}
169+
170+
std::wstring device_info_set::get_device_path(const std::wstring& instance_id, const GUID& interface_guid) {
171+
172+
// get device info set for instance
173+
HDEVINFO dev_info_set = SetupDiGetClassDevsW(&interface_guid, instance_id.c_str(), nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
174+
if (dev_info_set == INVALID_HANDLE_VALUE)
175+
usb_error::throw_error("internal error (SetupDiGetClassDevsW)");
176+
177+
// ensure the result is destroyed when the scope is left
178+
auto dev_info_set_guard = make_scope_exit([dev_info_set]() {
179+
SetupDiDestroyDeviceInfoList(dev_info_set);
180+
});
181+
182+
// retrieve first element of enumeration
183+
SP_DEVICE_INTERFACE_DATA dev_intf_data = { sizeof(dev_intf_data) };
184+
if (!SetupDiEnumDeviceInterfaces(dev_info_set, nullptr, &interface_guid, 0, &dev_intf_data))
185+
usb_error::throw_error("internal error (SetupDiEnumDeviceInterfaces)");
186+
187+
// retrieve path
188+
uint8_t dev_path_buf[MAX_PATH * sizeof(WCHAR) + sizeof(DWORD)];
189+
memset(dev_path_buf, 0, sizeof(dev_path_buf));
190+
PSP_DEVICE_INTERFACE_DETAIL_DATA_W intf_detail_data = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(dev_path_buf);
191+
intf_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
192+
if (!SetupDiGetDeviceInterfaceDetailW(dev_info_set, &dev_intf_data, intf_detail_data, sizeof(dev_path_buf), nullptr, nullptr))
193+
throw usb_error("Internal error (SetupDiGetDeviceInterfaceDetailA)", GetLastError());
194+
195+
return intf_detail_data->DevicePath;
196+
}
197+
198+
std::wstring device_info_set::get_device_path_by_guid(const std::wstring& instance_id) {
199+
auto device_guids = find_device_interface_guids();
200+
201+
CLSID clsid{};
202+
// use GUIDs to get device path
203+
for (const std::wstring& guid : device_guids) {
204+
if (CLSIDFromString(guid.c_str(), &clsid) != NOERROR)
205+
continue;
206+
207+
try {
208+
return get_device_path(instance_id.c_str(), clsid);
209+
}
210+
catch (usb_error&) {
211+
// ignore and try next one
212+
}
213+
}
214+
215+
return {};
216+
}
217+
218+
std::vector<std::wstring> device_info_set::find_device_interface_guids() {
219+
HKEY reg_key = SetupDiOpenDevRegKey(dev_info_set_, &dev_info_data_, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
220+
if (reg_key == INVALID_HANDLE_VALUE)
221+
throw usb_error("Cannot open device registry key", GetLastError());
222+
223+
auto reg_key_guard = make_scope_exit([reg_key]() {
224+
RegCloseKey(reg_key);
225+
});
226+
227+
// read registry value (without buffer, to query length)
228+
DWORD value_type = 0;
229+
DWORD value_size = 0;
230+
LSTATUS res = RegQueryValueExW(reg_key, L"DeviceInterfaceGUIDs", nullptr, &value_type, nullptr, &value_size);
231+
if (res == ERROR_FILE_NOT_FOUND)
232+
return std::vector<std::wstring>();
233+
if (res != 0 && res != ERROR_MORE_DATA)
234+
throw usb_error("Internal error (RegQueryValueExW)", res);
235+
236+
std::vector<uint8_t> str_list_raw;
237+
str_list_raw.resize(value_size);
238+
239+
// read registry value (with buffer)
240+
res = RegQueryValueExW(reg_key, L"DeviceInterfaceGUIDs", nullptr, &value_type, &str_list_raw[0], &value_size);
241+
if (res != 0)
242+
throw usb_error("Internal error (RegQueryValueExW)", res);
243+
244+
return split_string_list(reinterpret_cast<const wchar_t*>(&str_list_raw[0]));
245+
}

0 commit comments

Comments
 (0)