You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过WinAPI从设备管理器中筛选获取指定类型设备?

如何通过WinAPI从设备管理器中筛选获取指定类型设备?

看起来你现在用SetupDiGetClassDevsW加上DIGCF_ALLCLASSES拿到了所有存在的设备,范围确实太广了。其实有更精准的方式来筛选你需要的设备类型,不用自己挨个对比,我给你两种可行的方案,优先推荐第一种,更符合Windows设备枚举的设计思路:

一、使用设备类GUID精准筛选(推荐)

Windows系统里每个标准设备类型都有对应的官方类GUID,比如键盘、鼠标、显示器这些都有明确的定义。你可以直接把目标设备的GUID传给SetupDiGetClassDevsW,而不是传NULLDIGCF_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;
}

代码关键点说明

  1. 替换SetupDiGetClassDevsW的第一个参数为目标设备的GUID,同时去掉DIGCF_ALLCLASSES,因为我们已经指定了具体类
  2. 使用SetupDiEnumDeviceInterfaces来枚举设备接口(而不是你原来的SetupDiEnumDeviceInfo),因为设备接口类的枚举需要用这个函数
  3. 通过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

火山引擎 最新活动