什么可能导致SetupDiGetClassDevs不返回任何设备接口? (Windows,C ++)

时间:2015-09-03 00:16:50

标签: c++ windows unity3d usb hid

我已经在这个问题上工作了一个多月了。

我想做什么: 获取连接到系统的HID设备列表,不会出现故障。

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

背景: 我是在Unity 3D(游戏引擎)下运行的输入系统的作者。我的一个客户在他的(巨大的)项目中遇到操纵杆热插拔的问题。在他的游戏的标题屏幕上,一切都在游泳。加载第一个游戏关卡后,Windows会间歇性地(并且看似随机)报告零HID设备。只要当前控制器仍然连接,输入就可以正常工作,但是在这些“#34; failure"”期间,如果控制器被移除并重新插入,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之后。我已经阅读了直接输入和堆内存耗尽的一些问题,导致它返回OK但没有设备的结果,但据我所知,内存与此问题之间没有关系(除此之外)事实上,当内存大约为500MB时,它永远不会出现在标题屏幕上,只有在级别加载超过1.6GB之后才会开始看到它 - 但同样,我无法访问项目以确定它是否已经过了一些插件/组件导致这种情况。)

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

Unity基于Mono的古老版本,所以我想可能存在问题,但是我试图通过启动原生线程进行测试来规避Mono的问题表明它并不是真的有所作为。

经过这么长时间的努力,我已经在网上搜索了一些信息,但找不到任何与此问题类似的东西。

编辑:忘记提及,这个项目也使用Steam。

1 个答案:

答案 0 :(得分:0)

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