【发布时间】: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