Я работаю над этой проблемой уже более месяца.Что может заставить SetupDiGetClassDevs не возвращать интерфейсы устройств? (Windows, C++)
Что я пытаюсь сделать: Получите список устройств HID, подключенных к системе без сбоев.
Что происходит: SetupDiGetClassDevs периодически прерывает работу устройств (при фильтрации для устройств HID), но только для конкретного процесса, о котором идет речь. Другие процессы, работающие одновременно с одними и теми же вызовами, работают отлично. Я не могу воспроизвести эту проблему в чистом проекте, как бы я ни старался.
Справочная информация: Я являюсь автором системы ввода, которая работает под Unity 3D (игровым движком). У моего клиента проблемы с горячим подключением джойстика в его (гигантском) проекте. На титульном экране его игры все работает плавно. Когда загружается первый игровой уровень, Windows с перерывами (и, казалось бы, случайно) сообщает о нулевых HID-устройствах. Пока текущий контроллер все еще подключен, вход работает нормально, но в течение этих периодов «сбоя», если контроллер удален и снова подключен, Windows возвращает 0 интерфейсов устройства.
У меня нет доступа к проекту этого клиента (более 80 ГБ), но вы добавили мой собственный тестовый код в исполняемый файл, чтобы попытаться найти источник проблемы. У меня более тысячи клиентов, и только этот проект, похоже, имеет эту проблему.
Вот некоторый код тестирования, который я использую в 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 is C# based, поэтому я пробовал оба вызова этой функции каждый кадр из управляемого кода и путем вращения нового потока в 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 с перерывами. Опять же, это происходит только после того, как загружен большой уровень и объем памяти в игре превышает 2 ГБ.Я прочитал некоторые проблемы с Direct Input и исчерпал память кучи, заставив его вернуть результат OK, но без каких-либо устройств, но насколько я могу судить, нет никакой связи между памятью и этой проблемой (за исключением того факта, что она НИКОГДА происходит на экране заголовка, когда память ~ 500 МБ, только после того, как уровень загружается до 1,6 ГБ или около того, вы начинаете его видеть, - но опять же, у меня нет доступа к проекту, чтобы определить, есть ли там какой-то плагин/компонент, вызывающий это .)
Просто для повторного использования: Если я одновременно запускаю параллельный процесс (еще один экземпляр Unity с использованием другого проекта) и наблюдаю за журналами, я получаю правильное значение из 6 устройств в любое время в одном и том же момент, когда этот процесс возвращает 0. Такое же поведение согласуется с функциями HID Windows, Direct Input и XInput. Когда кто-то терпит неудачу, все сбой. Тем не менее, другие процессы, запущенные одновременно, НИКОГДА не имеют проблемы со всеми устройствами в каждом кадре.
Unity основан на древней версии Mono, поэтому я думаю, что там могут быть проблемы, но моя попытка обойти проблемы Mono, открутив собственный поток, чтобы сделать тестирование, показывает, что он действительно не делает любая разница.
Работая над этим так долго, я просматривал веб-информацию, но не могу найти ничего, что бы отдаленно напоминало эту проблему.
Редактировать: Забыл упоминать, этот проект также использует Steam.