2015-09-03 3 views
2

Я работаю над этой проблемой уже более месяца.Что может заставить 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.

ответ

0

Исправление состоит в том, чтобы заставить MS-код выделять память ниже 0x80000000 путем выделения нашей отбрасываемой кучи выше 0x80000000, если ваша программа работает на 64-битной ОС.