【问题标题】:What can cause SetupDiGetClassDevs to return no device interfaces? (Windows, C++)什么会导致 SetupDiGetClassDevs 不返回设备接口? (Windows、C++)
【发布时间】:2026-01-08 18:35:01
【问题描述】:

我已经研究这个问题一个多月了。

我想做什么: 获取连接到系统且未发生故障的 HID 设备列表。

发生了什么: SetupDiGetClassDevs 间歇性地不返回任何设备(针对 HID 设备进行过滤时),但仅针对有问题的特定进程。同时运行相同调用的其他进程运行良好。无论我多么努力,我都无法在干净的项目中重现此问题。

背景: 我是在 Unity 3D(游戏引擎)下运行的输入系统的作者。我的一个客户在他的(巨大的)项目中遇到了操纵杆热插拔的问题。在他的游戏的标题屏幕上,一切都很顺利。加载第一个游戏关卡后,Windows 会间歇性地(并且看似随机地)报告零 HID 设备。只要当前控制器仍然连接,输入就可以正常工作,但是在这些“故障”期间,如果控制器被移除并重新插入,Windows 将返回 0 个设备接口。

我无权访问此客户的项目(超过 80 GB),但已将我自己的测试代码注入可执行文件以尝试查找问题的根源。我有超过一千个客户,似乎只有这个项目有这个问题。

这是我在 C++ DLL 中使用的一些测试代码,用于查询系统中 HID 设备的数量。

int EnumerateDevices() {

int deviceCount = 0;

LPGUID guid = (LPGUID)malloc(sizeof(GUID));
HidD_GetHidGuid(guid);

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

if (deviceInfoSet != INVALID_HANDLE_VALUE) {

    SP_DEVINFO_DATA* deviceInfoData = new SP_DEVINFO_DATA();
    deviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
    deviceInfoData->DevInst = 0;

    DWORD deviceIndex = 0;
    int totalDeviceCount = 0;
    int totalInterfaceCount = 0;

    while (SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, deviceInfoData)) {
        deviceIndex += 1;

        SP_DEVICE_INTERFACE_DATA* deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
        deviceInterfaceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        int deviceInterfaceIndex = 0;

        while (SetupDiEnumDeviceInterfaces(deviceInfoSet, deviceInfoData, guid, deviceInterfaceIndex, deviceInterfaceData)) {
            deviceInterfaceIndex++;
            deviceCount++;
            totalInterfaceCount++;
        }

        totalDeviceCount++;
    }
    stringstream ssss;

    if(deviceIndex == 0) {
        ssss << "No devices. Last error = " << GetLastError() << "/n";
    }


    ssss << "Total Device Count = " << totalDeviceCount << " Total Interface Count = " << totalInterfaceCount;
    Log(ssss.str());

    SetupDiDestroyDeviceInfoList(deviceInfoSet);
    delete(deviceInfoData);
} else {
    stringstream sss;
    sss << "Invalid handle value!";
    Log(sss.str());
}

free(guid);

return deviceCount;

}

INVALID_HANDLE_VALUE 永远不会返回结果总是好的。但是在某些时期,Windows 将设备数报告为 0。SetupDiEnumDeviceInfo 返回 false,错误代码为 259 (ERROR_NO_MORE_ITEMS)。

Unity 是基于 C# 的,因此我尝试从托管代码的每一帧调用此函数,并在 C++ DLL 中启动一个新线程并记录结果。无论如何,每次都没有失败,在第一关的长时间加载期间,报告的设备将变为 0。并且在整个游戏过程中间歇性地,结果将在 6(系统中的总设备数)和 0 之间交替。有时结果为 0一次可以坚持几分钟,有时只会持续一帧。

使这个问题更有趣的是 XInput 和 Direct Input 也将在与此代码完全相同的时间失败。 XInput 和 DI 是独立的,但我怀疑它们在内部进行了相同的 Windows 函数调用。显然这个问题不在我的代码中,但我不知道是什么可能导致这种情况发生。我怀疑其他一些插件正在干扰或可能是内存不足问题。

其他信息: 如果我将对 SetupDiGetClassDevs 的调用更改为:

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);

我总是得到一个有效的结果返回——168。这永远不会改变。然而,这些都不是设备接口,所以我不能使用这些信息。如果我添加回设备接口过滤器,我会间歇性地得到 0。同样,这只发生在加载大关卡并且游戏的内存占用超过 2GB 之后。我已经阅读了 Direct Input 的一些问题并且堆内存不足导致它返回 OK 但没有设备的结果,但据我所知,内存和这个问题之间没有关系(除了它从来没有当内存约为 500MB 时,会在标题屏幕上出现,只有在关卡加载超过 1.6GB 之后您才会开始看到它——但同样,我无法访问该项目以确定它是否是其中的某个插件/组件导致这个.)

再次声明: 如果我同时运行一个并行进程(使用不同项目的另一个 Unity 实例)并查看日志,我会在该进程返回 0 的同一时刻始终收到 6 个设备的正确值。这种相同的行为与 Windows 一致HID 功能、直接输入和 XInput。当一个失败时,所有失败。然而,同时运行的其他进程在每帧查看所有设备时都不会遇到问题。

Unity 基于 Mono 的一个古老版本,所以我想那里可能存在问题,但我试图通过启动一个本地线程来进行测试来规避 Mono 的问题,结果表明它并没有真正产生任何问题区别。

在这个问题上工作了这么久,我在网上搜索了信息,但找不到任何与这个问题很相似的东西。

编辑:忘了说,这个项目也使用了 Steam。

【问题讨论】:

    标签: c++ windows unity3d usb hid


    【解决方案1】:

    如果您的程序在 64 位操作系统上运行,则修复方法是强制 MS 代码分配低于 0x80000000 的内存,方法是分配高于 0x80000000 的可丢弃堆。

    【讨论】:

      最近更新 更多