summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface
diff options
context:
space:
mode:
authorEmptyChaos <EmptyChaos@users.noreply.github.com>2016-08-10 23:30:36 +0000
committerEmptyChaos <EmptyChaos@users.noreply.github.com>2016-08-11 09:30:36 +1000
commit17e4bd9575281052f9aa27678c41e2e94e6c06e9 (patch)
tree85910cedc9599fb55838ad61219486020fe4e66b /Source/Core/InputCommon/ControllerInterface
parent8552ff5789a2498a4ede1e811710dc1d3cba3d6b (diff)
InputCommon: Fix slow startup on some Windows systems
Rewrite GetXInputGUIDS to use SetupAPI instead of WMI Queries. When using a language pack where the system language and user/program language differ, Windows starts taking a VERY long time (10+ seconds) to complete Queries for Win32_PNPEntity objects (it's probably translating every single string since it transfers every single one from the WMI server into memory in the program). Fixes Issue 9744.
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp7
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.cpp172
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.h4
3 files changed, 65 insertions, 118 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp
index 92798a055c..9968bc798f 100644
--- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp
@@ -23,14 +23,11 @@ void InitJoystick(IDirectInput8* const idi8, HWND hwnd)
idi8->EnumDevices(DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks,
DIEDFL_ATTACHEDONLY);
- std::vector<DWORD> xinput_guids;
- GetXInputGUIDS(&xinput_guids);
-
+ std::unordered_set<DWORD> xinput_guids = GetXInputGUIDS();
for (DIDEVICEINSTANCE& joystick : joysticks)
{
// skip XInput Devices
- if (std::find(xinput_guids.begin(), xinput_guids.end(), joystick.guidProduct.Data1) !=
- xinput_guids.end())
+ if (xinput_guids.count(joystick.guidProduct.Data1))
{
continue;
}
diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.cpp
index d6f1cab257..246501dd70 100644
--- a/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.cpp
@@ -2,131 +2,81 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-// This function is contained in a separate file because WbemIdl.h pulls in files which break on
-// /Zc:strictStrings, so this compilation unit is compiled without /Zc:strictStrings.
-
-#include <WbemIdl.h>
-#include <Windows.h>
+#include <cwchar>
+#include <unordered_set>
#include <vector>
-#define SAFE_RELEASE(p) \
- { \
- if (p) \
- { \
- (p)->Release(); \
- (p) = nullptr; \
- } \
- }
+// clang-format off
+#include <Windows.h>
+#include <SetupAPI.h>
+// clang-format on
namespace ciface
{
namespace DInput
{
-//-----------------------------------------------------------------------------
-// Modified some MSDN code to get all the XInput device GUID.Data1 values in a vector,
-// faster than checking all the devices for each DirectInput device, like MSDN says to do
-//-----------------------------------------------------------------------------
-void GetXInputGUIDS(std::vector<DWORD>* guids)
+// Code for enumerating hardware devices that use the XINPUT device driver.
+// The MSDN recommended code suffers from massive performance problems when using language packs,
+// if the system and user languages differ then WMI Queries become incredibly slow (multiple
+// seconds). This is more or less equivalent and much faster.
+std::unordered_set<DWORD> GetXInputGUIDS()
{
- IWbemLocator* pIWbemLocator = nullptr;
- IEnumWbemClassObject* pEnumDevices = nullptr;
- IWbemClassObject* pDevices[20] = {0};
- IWbemServices* pIWbemServices = nullptr;
- BSTR bstrNamespace = nullptr;
- BSTR bstrDeviceID = nullptr;
- BSTR bstrClassName = nullptr;
- DWORD uReturned = 0;
- VARIANT var;
- HRESULT hr;
-
- // CoInit if needed
- hr = CoInitialize(nullptr);
- bool bCleanupCOM = SUCCEEDED(hr);
-
- // Create WMI
- hr = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER,
- __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
- if (FAILED(hr) || pIWbemLocator == nullptr)
- goto LCleanup;
-
- bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2");
- if (bstrNamespace == nullptr)
- goto LCleanup;
- bstrClassName = SysAllocString(L"Win32_PNPEntity");
- if (bstrClassName == nullptr)
- goto LCleanup;
- bstrDeviceID = SysAllocString(L"DeviceID");
- if (bstrDeviceID == nullptr)
- goto LCleanup;
-
- // Connect to WMI
- hr = pIWbemLocator->ConnectServer(bstrNamespace, nullptr, nullptr, 0L, 0L, nullptr, nullptr,
- &pIWbemServices);
- if (FAILED(hr) || pIWbemServices == nullptr)
- goto LCleanup;
-
- // Switch security level to IMPERSONATE.
- CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
- RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
-
- hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, nullptr, &pEnumDevices);
- if (FAILED(hr) || pEnumDevices == nullptr)
- goto LCleanup;
-
- // Loop over all devices
- while (true)
+ static const GUID s_GUID_devclass_HID = {
+ 0x745a17a0, 0x74d3, 0x11d0, {0xb6, 0xfe, 0x00, 0xa0, 0xc9, 0x0f, 0x57, 0xda}};
+ std::unordered_set<DWORD> guids;
+
+ // Enumerate everything under the "Human Interface Devices" tree in the Device Manager
+ // NOTE: Some devices show up multiple times due to sub-devices, we rely on the set to
+ // prevent duplicates.
+ HDEVINFO setup_enum = SetupDiGetClassDevsW(&s_GUID_devclass_HID, nullptr, nullptr, DIGCF_PRESENT);
+ if (setup_enum == INVALID_HANDLE_VALUE)
+ return guids;
+
+ std::vector<wchar_t> buffer(128);
+ SP_DEVINFO_DATA dev_info;
+ dev_info.cbSize = sizeof(SP_DEVINFO_DATA);
+ for (DWORD i = 0; SetupDiEnumDeviceInfo(setup_enum, i, &dev_info); ++i)
{
- // Get 20 at a time
- hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned);
- if (FAILED(hr) || uReturned == 0)
- break;
-
- for (UINT iDevice = 0; iDevice < uReturned; ++iDevice)
+ // Need to find the size of the data and set the buffer appropriately
+ DWORD buffer_size = 0;
+ while (!SetupDiGetDeviceRegistryPropertyW(setup_enum, &dev_info, SPDRP_HARDWAREID, nullptr,
+ reinterpret_cast<BYTE*>(buffer.data()),
+ static_cast<DWORD>(buffer.size()), &buffer_size))
{
- // For each device, get its device ID
- hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, nullptr, nullptr);
- if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != nullptr)
- {
- // Check if the device ID contains "IG_". If it does, then it's an XInput device
- // This information can not be found from DirectInput
- if (wcsstr(var.bstrVal, L"IG_"))
- {
- // If it does, then get the VID/PID from var.bstrVal
- DWORD dwPid = 0, dwVid = 0;
- WCHAR* strVid = wcsstr(var.bstrVal, L"VID_");
- if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1)
- dwVid = 0;
- WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
- if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1)
- dwPid = 0;
+ if (buffer_size > buffer.size())
+ buffer.resize(buffer_size);
+ else
+ break;
+ }
+ if (GetLastError() != ERROR_SUCCESS)
+ continue;
- // Compare the VID/PID to the DInput device
- DWORD dwVidPid = MAKELONG(dwVid, dwPid);
- guids->push_back(dwVidPid);
- // bIsXinputDevice = true;
- }
- }
- SAFE_RELEASE(pDevices[iDevice]);
+ // HARDWAREID is a REG_MULTI_SZ
+ // There are multiple strings separated by NULs, the list is ended by an empty string.
+ for (std::size_t j = 0; buffer[j]; j += std::wcslen(&buffer[j]) + 1)
+ {
+ // XINPUT devices have "IG_xx" embedded in their IDs which is what we look for.
+ if (!std::wcsstr(&buffer[j], L"IG_"))
+ continue;
+
+ unsigned int vid = 0;
+ unsigned int pid = 0;
+
+ // Extract Vendor and Product IDs for matching against DirectInput's device list.
+ wchar_t* pos = std::wcsstr(&buffer[j], L"VID_");
+ if (!pos || !std::swscanf(pos, L"VID_%4X", &vid))
+ continue;
+ pos = std::wcsstr(&buffer[j], L"PID_");
+ if (!pos || !std::swscanf(pos, L"PID_%4X", &pid))
+ continue;
+
+ guids.insert(MAKELONG(vid, pid));
+ break;
}
}
-LCleanup:
- if (bstrNamespace)
- SysFreeString(bstrNamespace);
- if (bstrDeviceID)
- SysFreeString(bstrDeviceID);
- if (bstrClassName)
- SysFreeString(bstrClassName);
- for (UINT iDevice = 0; iDevice < 20; iDevice++)
- SAFE_RELEASE(pDevices[iDevice]);
- SAFE_RELEASE(pEnumDevices);
- SAFE_RELEASE(pIWbemLocator);
- SAFE_RELEASE(pIWbemServices);
-
- if (bCleanupCOM)
- CoUninitialize();
+ SetupDiDestroyDeviceInfoList(setup_enum);
+ return guids;
}
}
}
-
-#undef SAFE_RELEASE
diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.h b/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.h
index be075deb9b..232887404c 100644
--- a/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.h
+++ b/Source/Core/InputCommon/ControllerInterface/DInput/XInputFilter.h
@@ -5,12 +5,12 @@
#pragma once
#include <Windows.h>
-#include <vector>
+#include <unordered_set>
namespace ciface
{
namespace DInput
{
-void GetXInputGUIDS(std::vector<DWORD>* guids);
+std::unordered_set<DWORD> GetXInputGUIDS();
}
}