libusb 当前版本(1.0.26)libusb.h 头文件提供的接口似乎没有办法获取 Windows 平台相关的设备实例路径,其形如:
\\?\usb#vid_04ca&pid_7070#5&20d34a76&0&6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
只是提供了 libusb_get_port_numbers 之类的接口来获取拓扑结构。
我们可以通过 libusb 源码中平台相关的接口来获取DevicePath,但是使用非公有接口意味着替换版本的时候要注意源码相关的修改。目前找了两种方式:
通过 libusbi.h 头文件中的 usbi_get_device_priv 函数获取 libusb_device 对应的平台相关的数据结构,对应到 windows 平台就是 winusb_device_priv,其包含的 path 字段就是存放DevicePath。
- #include "libusb.h"
- #include "libusbi.h"
-
- void enum_device()
- {
- libusb_init(NULL);
- libusb_device **device_list;
- int count = libusb_get_device_list(NULL, &device_list);
- for(int i = 0 ; i < count; i++)
- {
- libusb_device_descriptor desc;
- int ret = libusb_get_device_descriptor(device_list[i], &desc);
- if (ret != LIBUSB_SUCCESS) {
- continue;
- }
- winusb_device_priv *priv = (winusb_device_priv*)usbi_get_device_priv(device_list[i]);
- if (priv) {
- fprintf(stderr, "Device %d path: %s.\n", i, priv->path);
- }
- }
- libusb_free_device_list(device_list, 1);
- libusb_exit(NULL);
- }
从 usbi_get_device_priv 函数推测,公有结构和平台相关结构在内存上是紧挨着的:
- #define PTR_ALIGN(v) (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
-
- static inline void *usbi_get_device_priv(struct libusb_device *dev)
- {
- return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev));
- }
获取 DevicePath 的过程在 windows_winusb.c 的 winusb_get_device_list 和 get_interface_details 函数,使用的 SetupAPI 系列的接口来获取的。有一点需要注意,获取到的 path 内部转换成了大写,见 normalize_path 函数实现。
不修改库源码,而是直接使用私有接口需要用到的源码文件:

其中 config.h 文件来自于源码的 msvc 文件夹。
可能会遇到类型转换报错,自己做下显式转换即可。

session_data 在 windows 平台上目前就是存储的 SP_DEVINFO_DATA 结构的 DevInst 值。相较于第一种方式,我们需要再次用 SetupAPI 的接口枚举一遍设备信息,然后判断 session_data 和 DevInst 是否相等,相等时就可以用 Win32 接口获取 DevicePath 了。
libusb_device 结构体定义在 libusbi.h 头文件。
- #include
- #include
- #include
- #pragma comment(lib, "SetupAPI.lib")
- #include
- // 具体的设备 GUID 需要 initguid, 如 usbiodef
- #include
- // USB 设备
- // GUID_DEVINTERFACE_USB_DEVICE
- #include
-
- void enum_device()
- {
- // HDEVINFO 标识设备信息集
- HDEVINFO info_set;
- // SetupDiGetClassDevs 返回包含本地计算机请求的设备信息元素的设备信息集的句柄
- // 接口文档:https://learn.microsoft.com/zh-cn/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsa
- // 若要返回支持任何类的设备接口的设备,设置 DIGCF_DEVICEINTERFACE 和 DIGCF_ALLCLASSES 标志,然后将 ClassGuid 设置为 NULL
- //info_set = SetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
- // 若要仅返回支持指定类的设备接口的设备,设置 DIGCF_DEVICEINTERFACE 标志并使用 ClassGuid 参数提供设备接口类的类 GUID
- // 实际使用对应设备的 GUID
- GUID device_guid{GUID_DEVINTERFACE_USB_DEVICE};
- info_set = SetupDiGetClassDevsA(&device_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
- // 如果操作失败,返回 INVALID_HANDLE_VALUE
- if (info_set == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "SetupDiGetClassDevs: [err code] %d.\n", GetLastError());
- return;
- }
-
- // SP_DEVINFO_DATA 标识设备信息集中的设备
- SP_DEVINFO_DATA info_data = { 0 };
- info_data.cbSize = sizeof(info_data);
- // SetupDiEnumDeviceInfo 枚举设备信息
- for (int index = 0; SetupDiEnumDeviceInfo(info_set, index, &info_data); index++)
- {
- fprintf(stderr, "Device %d %d:\n", index, info_data.DevInst);
-
- // 获取设备实例路径
- // SP_DEVICE_INTERFACE_DATA 设备信息集中的设备接口
- SP_DEVICE_INTERFACE_DATA interface_data = { 0 };
- interface_data.cbSize = sizeof(interface_data);
- // SetupDiEnumDeviceInterfaces 枚举包含在设备信息集中的设备接口
- BOOL ret = SetupDiEnumDeviceInterfaces(info_set, NULL, (LPGUID)&device_guid, index, &interface_data);
- if (!ret) continue;
- ULONG required_len = 0;
- // SetupDiGetDeviceInterfaceDetail 返回有关设备接口的详细信息
- // 第一次调用是获取长度,这里是返回false
- SetupDiGetDeviceInterfaceDetailA(info_set, &interface_data, NULL, 0, &required_len, NULL);
- if (required_len <= 0) continue;
- ULONG predicted_len = required_len;
- // SP_INTERFACE_DEVICE_DETAIL_DATA 包含设备接口的路径
- SP_INTERFACE_DEVICE_DETAIL_DATA_A detail_data = { 0 };
- detail_data.cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A);
- // 检索即插即用设备信息
- if (SetupDiGetDeviceInterfaceDetailA(info_set,
- &interface_data,
- &detail_data,
- predicted_len,
- &required_len,
- &info_data)) {
- fprintf(stderr, "Device Instance Path: %s.\n", detail_data.DevicePath);
- }
- }
-
- // SetupDiDestroyDeviceInfoList 删除设备信息集并释放所有关联的内存
- SetupDiDestroyDeviceInfoList(info_set);
- }