枚举设备信息主要通过 SetupAPI 模块中的接口来实现,配合对应的设备类 GUID。
这里以枚举 USB 设备信息为例,包含设备实例地址等:
#include <stdio.h>
#include <Windows.h>
#include <SetupAPI.h>
#pragma comment(lib, "SetupAPI.lib")
#include <devguid.h>
// 具体的设备 GUID 需要 initguid, 如 usbiodef
#include <initguid.h>
// USB 设备
// GUID_DEVINTERFACE_USB_DEVICE
#include <usbiodef.h>
void enum_device_info()
{
// 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);
constexpr DWORD buf_size = 2048;
BYTE info_buf[buf_size + 1] = { 0 };
// SetupDiEnumDeviceInfo 枚举设备信息
for (int index = 0; SetupDiEnumDeviceInfo(info_set, index, &info_data); index++)
{
fprintf(stderr, "Device %d:\n", index);
// SetupDiGetDeviceRegistryProperty 检索指定的即插即用设备属性
// SPDRP_* 参数这里只列举了一部分
// 如果返回 ERROR_INSUFFICIENT_BUFFER 错误,表示缓冲区不够存放数据,根据最后一个参数返回的大小重新设置缓冲区
// 设备描述
// SPDRP_DEVICEDESC 函数检索包含设备说明的 REG_SZ 字符串
if (!SetupDiGetDeviceRegistryPropertyA(info_set, &info_data, SPDRP_DEVICEDESC, NULL, info_buf, buf_size, NULL)) {
fprintf(stderr, "Device Desc: [err code] %d %d.\n", GetLastError(), ERROR_INSUFFICIENT_BUFFER);
} else {
fprintf(stderr, "Device Desc: %s.\n", info_buf);
}
// 设备设备类
// SPDRP_CLASS 函数检索包含设备的设备设置类的 REG_SZ 字符串
if (!SetupDiGetDeviceRegistryPropertyA(info_set, &info_data, SPDRP_CLASS, NULL, info_buf, buf_size, NULL)) {
fprintf(stderr, "Device Class: [err code] %d.\n", GetLastError());
} else {
fprintf(stderr, "Device Class: %s.\n", info_buf);
}
// 设备设置类 GUID
// SPDRP_CLASSGUID 函数检索一个 REG_SZ 字符串,该字符串包含表示设备的设备设置类的 GUID
if (!SetupDiGetDeviceRegistryPropertyA(info_set, &info_data, SPDRP_CLASSGUID, NULL, info_buf, buf_size, NULL)) {
fprintf(stderr, "Device Class GUID: [err code] %d.\n", GetLastError());
} else {
fprintf(stderr, "Device Class GUID: %s.\n", info_buf);
}
// 设备友好名称
// SPDRP_FRIENDLYNAME 函数检索包含设备的友好名称的 REG_SZ 字符串
if (!SetupDiGetDeviceRegistryPropertyA(info_set, &info_data, SPDRP_FRIENDLYNAME, NULL, info_buf, buf_size, NULL)) {
fprintf(stderr, "Device Friendly Name: [err code] %d.\n", GetLastError());
} else {
fprintf(stderr, "Device Friendly Name: %s.\n", info_buf);
}
// 返回硬件 ID 列表
// SPDRP_HARDWAREID 函数检索包含设备硬件 ID 列表的 REG_MULTI_SZ 字符串
if (!SetupDiGetDeviceRegistryPropertyA(info_set, &info_data, SPDRP_HARDWAREID, NULL, info_buf, buf_size, NULL)) {
fprintf(stderr, "Device Hardware ID: [err code] %d.\n", GetLastError());
} else {
fprintf(stderr, "Device Hardware ID: %s.\n", info_buf);
}
// 获取设备实例 ID
// SetupDiGetDeviceInstanceIdA 函数检索与设备信息元素关联的设备实例 ID
if (!SetupDiGetDeviceInstanceIdA(info_set, &info_data, (PSTR)info_buf, buf_size, NULL)) {
fprintf(stderr, "Device Instance ID: [err code] %d.\n", GetLastError());
} else {
fprintf(stderr, "Device Instance ID: %s.\n", info_buf);
}
// 获取设备实例路径
// 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);
}
输出结果:
Device 0:
Device Desc: USB Composite Device.
Device Class: USB.
Device Class GUID: {36fc9e60-c465-11cf-8056-444553540000}.
Device Friendly Name: [err code] 13. // 13 对应 ERROR_INVALID_DATA
Device Hardware ID: USB\VID_04CA&PID_7070&REV_0023.
Device Instance ID: USB\VID_04CA&PID_7070\5&20D34A76&0&6.
Device Instance Path: \\?\usb#vid_04ca&pid_7070#5&20d34a76&0&6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}.
Device 1:
Device Desc: Generic Bluetooth Adapter.
Device Class: Bluetooth.
Device Class GUID: {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}.
Device Friendly Name: [err code] 13.
Device Hardware ID: USB\VID_0CF3&PID_E500&REV_0001.
Device Instance ID: USB\VID_0CF3&PID_E500\5&20D34A76&0&14.
Device Instance Path: \\?\usb#vid_0cf3&pid_e500#5&20d34a76&0&14#{a5dcbf10-6530-11d2-901f-00c04fb951ed}.