什么可以导致SetupDiGetClassDevs不返回设备接口?(Windows、c++)

What can cause SetupDiGetClassDevs to return no device interfaces? (Windows, C++)

本文关键字:接口 Windows c++ SetupDiGetClassDevs 返回 什么      更新时间:2023-10-16

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

我想做什么:获取连接到系统的HID设备列表。

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

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

我不能访问这个客户的项目(超过80gb),但是我已经将我自己的测试代码注入到可执行文件中,试图找到问题的根源。我有一千多个客户,似乎只有这个项目有这个问题。

这是我在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;

}

结果总是OK, 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之后。我读过一些问题直接输入和堆内存耗尽导致返回一个结果好了但是没有设备,但据我所知没有记忆和之间的关系这一问题(除了事实没有发生屏幕上的标题~ 500 mb内存时,只有在过去的水平加载1.6 gb左右你开始看到它,但再一次,我没有访问项目来确定它的一些插件/组件造成。)

重申一下:如果我同时运行一个并行进程(使用不同项目的另一个Unity实例)并观察日志,我将在该进程返回0的同一时刻始终接收到6个设备的正确值。同样的行为与Windows HID函数、直接输入和XInput一致。一个人失败了,所有人都失败了。然而,同时运行的其他进程从来没有看到所有设备每帧的问题。

Unity是基于Mono的一个古老版本,所以我想那里可能会有问题,但我试图通过旋转一个本地线程来做测试来规避Mono的问题,这表明它并没有真正产生任何差异。

在这个问题上工作了这么长时间,我在网上搜索了很多信息,但没有找到任何类似这个问题的东西。

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

如果您的程序运行在64位操作系统上,则通过在0x80000000以上分配我们的可丢弃堆来强制MS代码分配低于0x80000000的内存。