如何通过WinAPI从设备管理器中筛选获取指定类型设备?
如何通过WinAPI从设备管理器中筛选获取指定类型设备?
看起来你现在用SetupDiGetClassDevsW加上DIGCF_ALLCLASSES拿到了所有存在的设备,范围确实太广了。其实有更精准的方式来筛选你需要的设备类型,不用自己挨个对比,我给你两种可行的方案,优先推荐第一种,更符合Windows设备枚举的设计思路:
一、使用设备类GUID精准筛选(推荐)
Windows系统里每个标准设备类型都有对应的官方类GUID,比如键盘、鼠标、显示器这些都有明确的定义。你可以直接把目标设备的GUID传给SetupDiGetClassDevsW,而不是传NULL和DIGCF_ALLCLASSES,这样就能直接枚举指定类的设备,效率更高,也更精准。
常见设备类型的GUID参考
你需要包含对应的头文件来获取这些预定义的GUID,或者也可以直接手动定义(如果头文件引入有问题的话):
- 键盘类:
GUID_DEVINTERFACE_KEYBOARD(需要包含<hidclass.h>) - 鼠标类:
GUID_DEVINTERFACE_MOUSE(需要包含<hidclass.h>) - 显示器类:
GUID_DEVINTERFACE_MONITOR(需包含<initguid.h>和<ddraw.h>) - 音频输出设备:
GUID_DEVINTERFACE_AUDIO_RENDER(需包含<mmdeviceapi.h>)
示例代码(以枚举键盘为例)
下面是修改后的代码,直接枚举当前存在的所有键盘设备,同时获取设备描述和设备路径:
#include <Windows.h> #include <SetupAPI.h> #include <initguid.h> #include <hidclass.h> // 引入键盘、鼠标的GUID定义 #include <stdio.h> #include <cfgmgr32.h> #include <iostream> #pragma comment(lib, "setupapi.lib") int main() { HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { sizeof(SP_DEVICE_INTERFACE_DATA) }; DWORD index = 0; // 传入键盘类GUID,只枚举当前存在的键盘设备接口 hDevInfo = SetupDiGetClassDevsW( &GUID_DEVINTERFACE_KEYBOARD, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE // 注意这里用DIGCF_DEVICEINTERFACE,对应设备接口类 ); if (hDevInfo == INVALID_HANDLE_VALUE) { printf("SetupDiGetClassDevsW 错误: 0x%X\n", GetLastError()); return 1; } // 枚举指定类的设备接口 while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_KEYBOARD, index++, &deviceInterfaceData)) { DWORD requiredSize = 0; // 先获取所需的缓冲区大小 SetupDiGetDeviceInterfaceDetailW(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL); // 分配内存存储设备接口详情 PSP_DEVICE_INTERFACE_DETAIL_DATA_W pDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)LocalAlloc(LPTR, requiredSize); if (!pDetailData) { wprintf(L"内存分配失败\n"); continue; } pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); SP_DEVINFO_DATA devInfoData = { sizeof(SP_DEVINFO_DATA) }; // 获取设备接口的详细信息(包括设备路径和设备信息) if (SetupDiGetDeviceInterfaceDetailW(hDevInfo, &deviceInterfaceData, pDetailData, requiredSize, NULL, &devInfoData)) { WCHAR deviceDesc[256] = { 0 }; // 获取设备友好描述 if (SetupDiGetDeviceRegistryPropertyW( hDevInfo, &devInfoData, SPDRP_DEVICEDESC, NULL, (PBYTE)deviceDesc, sizeof(deviceDesc), NULL)) { wprintf(L"键盘设备 %d: %s\n", index, deviceDesc); wprintf(L"设备路径: %s\n", pDetailData->DevicePath); } } LocalFree(pDetailData); wprintf(L"--------------------------------\n"); } if (GetLastError() != ERROR_NO_MORE_ITEMS) { printf("枚举设备错误: 0x%X\n", GetLastError()); } SetupDiDestroyDeviceInfoList(hDevInfo); return 0; }
代码关键点说明
- 替换
SetupDiGetClassDevsW的第一个参数为目标设备的GUID,同时去掉DIGCF_ALLCLASSES,因为我们已经指定了具体类 - 使用
SetupDiEnumDeviceInterfaces来枚举设备接口(而不是你原来的SetupDiEnumDeviceInfo),因为设备接口类的枚举需要用这个函数 - 通过
SetupDiGetDeviceInterfaceDetailW可以获取设备的路径,如果你后续需要打开设备进行操作(比如读取键盘输入),这个路径是必须的
如果需要枚举多种类型的设备,只需要重复这段逻辑,传入不同的GUID即可。
二、自定义文本/ID匹配(特殊场景用)
如果有些设备没有标准的类GUID,或者你想更灵活地筛选(比如按设备描述关键词),可以保留你原来的全类枚举逻辑,然后在循环中加入判断条件,比如检查设备描述是否包含“键盘”“鼠标”等关键词,或者设备ID是否匹配特定前缀(比如HID\开头的HID设备)。
示例修改(在你原代码中加筛选)
// 原循环内的代码 while (SetupDiEnumDeviceInfo(hDevInfo, index++, &deviceInfo)) { WCHAR deviceID[256] = { 0 }; if (SetupDiGetDeviceInstanceIdW(hDevInfo, &deviceInfo, deviceID, 256, NULL)) { // 检查设备ID前缀,比如HID设备 if (wcsstr(deviceID, L"HID\\") != NULL) { wprintf(L"HID设备 %d: %s\n", index, deviceID); } } WCHAR desc[256] = { 0 }; if (SetupDiGetDeviceRegistryPropertyW( hDevInfo, &deviceInfo, SPDRP_DEVICEDESC, NULL, (PBYTE)desc, sizeof(desc), NULL)) { // 检查描述是否包含关键词(注意多语言问题,英文系统是"Keyboard") if (wcsstr(desc, L"键盘") != NULL || wcsstr(desc, L"Mouse") != NULL) { wprintf(L"匹配设备描述: %s\n", desc); } } wprintf(L"--------------------------------\n"); }
不过这种方法有个明显的缺点:依赖设备描述的本地化文本,比如英文系统里键盘是“Keyboard”,中文是“键盘”,跨语言环境下兼容性不好,所以除非特殊需求,还是优先用GUID筛选。
总结下,优先用设备类GUID来筛选,这是Windows官方推荐的方式,精准又高效;如果有特殊场景,再考虑自定义匹配逻辑。
内容来源于stack exchange




