Merge pull request #4111 from EmptyChaos/startup-perf-langs
InputCommon: Fix slow startup on some Windows systems with language packs (Issue 9744)
This commit is contained in:
commit
e38ddfd787
|
@ -23,14 +23,11 @@ void InitJoystick(IDirectInput8* const idi8, HWND hwnd)
|
||||||
idi8->EnumDevices(DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks,
|
idi8->EnumDevices(DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks,
|
||||||
DIEDFL_ATTACHEDONLY);
|
DIEDFL_ATTACHEDONLY);
|
||||||
|
|
||||||
std::vector<DWORD> xinput_guids;
|
std::unordered_set<DWORD> xinput_guids = GetXInputGUIDS();
|
||||||
GetXInputGUIDS(&xinput_guids);
|
|
||||||
|
|
||||||
for (DIDEVICEINSTANCE& joystick : joysticks)
|
for (DIDEVICEINSTANCE& joystick : joysticks)
|
||||||
{
|
{
|
||||||
// skip XInput Devices
|
// skip XInput Devices
|
||||||
if (std::find(xinput_guids.begin(), xinput_guids.end(), joystick.guidProduct.Data1) !=
|
if (xinput_guids.count(joystick.guidProduct.Data1))
|
||||||
xinput_guids.end())
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,131 +2,81 @@
|
||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
// This function is contained in a separate file because WbemIdl.h pulls in files which break on
|
#include <cwchar>
|
||||||
// /Zc:strictStrings, so this compilation unit is compiled without /Zc:strictStrings.
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <WbemIdl.h>
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define SAFE_RELEASE(p) \
|
// clang-format off
|
||||||
{ \
|
#include <Windows.h>
|
||||||
if (p) \
|
#include <SetupAPI.h>
|
||||||
{ \
|
// clang-format on
|
||||||
(p)->Release(); \
|
|
||||||
(p) = nullptr; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ciface
|
namespace ciface
|
||||||
{
|
{
|
||||||
namespace DInput
|
namespace DInput
|
||||||
{
|
{
|
||||||
//-----------------------------------------------------------------------------
|
// Code for enumerating hardware devices that use the XINPUT device driver.
|
||||||
// Modified some MSDN code to get all the XInput device GUID.Data1 values in a vector,
|
// The MSDN recommended code suffers from massive performance problems when using language packs,
|
||||||
// faster than checking all the devices for each DirectInput device, like MSDN says to do
|
// if the system and user languages differ then WMI Queries become incredibly slow (multiple
|
||||||
//-----------------------------------------------------------------------------
|
// seconds). This is more or less equivalent and much faster.
|
||||||
void GetXInputGUIDS(std::vector<DWORD>* guids)
|
std::unordered_set<DWORD> GetXInputGUIDS()
|
||||||
{
|
{
|
||||||
IWbemLocator* pIWbemLocator = nullptr;
|
static const GUID s_GUID_devclass_HID = {
|
||||||
IEnumWbemClassObject* pEnumDevices = nullptr;
|
0x745a17a0, 0x74d3, 0x11d0, {0xb6, 0xfe, 0x00, 0xa0, 0xc9, 0x0f, 0x57, 0xda}};
|
||||||
IWbemClassObject* pDevices[20] = {0};
|
std::unordered_set<DWORD> guids;
|
||||||
IWbemServices* pIWbemServices = nullptr;
|
|
||||||
BSTR bstrNamespace = nullptr;
|
|
||||||
BSTR bstrDeviceID = nullptr;
|
|
||||||
BSTR bstrClassName = nullptr;
|
|
||||||
DWORD uReturned = 0;
|
|
||||||
VARIANT var;
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
// CoInit if needed
|
// Enumerate everything under the "Human Interface Devices" tree in the Device Manager
|
||||||
hr = CoInitialize(nullptr);
|
// NOTE: Some devices show up multiple times due to sub-devices, we rely on the set to
|
||||||
bool bCleanupCOM = SUCCEEDED(hr);
|
// prevent duplicates.
|
||||||
|
HDEVINFO setup_enum = SetupDiGetClassDevsW(&s_GUID_devclass_HID, nullptr, nullptr, DIGCF_PRESENT);
|
||||||
|
if (setup_enum == INVALID_HANDLE_VALUE)
|
||||||
|
return guids;
|
||||||
|
|
||||||
// Create WMI
|
std::vector<wchar_t> buffer(128);
|
||||||
hr = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER,
|
SP_DEVINFO_DATA dev_info;
|
||||||
__uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
|
dev_info.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||||
if (FAILED(hr) || pIWbemLocator == nullptr)
|
for (DWORD i = 0; SetupDiEnumDeviceInfo(setup_enum, i, &dev_info); ++i)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
// Get 20 at a time
|
// Need to find the size of the data and set the buffer appropriately
|
||||||
hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned);
|
DWORD buffer_size = 0;
|
||||||
if (FAILED(hr) || uReturned == 0)
|
while (!SetupDiGetDeviceRegistryPropertyW(setup_enum, &dev_info, SPDRP_HARDWAREID, nullptr,
|
||||||
|
reinterpret_cast<BYTE*>(buffer.data()),
|
||||||
|
static_cast<DWORD>(buffer.size()), &buffer_size))
|
||||||
|
{
|
||||||
|
if (buffer_size > buffer.size())
|
||||||
|
buffer.resize(buffer_size);
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
if (GetLastError() != ERROR_SUCCESS)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (UINT iDevice = 0; iDevice < uReturned; ++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)
|
||||||
{
|
{
|
||||||
// For each device, get its device ID
|
// XINPUT devices have "IG_xx" embedded in their IDs which is what we look for.
|
||||||
hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, nullptr, nullptr);
|
if (!std::wcsstr(&buffer[j], L"IG_"))
|
||||||
if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != nullptr)
|
continue;
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Compare the VID/PID to the DInput device
|
unsigned int vid = 0;
|
||||||
DWORD dwVidPid = MAKELONG(dwVid, dwPid);
|
unsigned int pid = 0;
|
||||||
guids->push_back(dwVidPid);
|
|
||||||
// bIsXinputDevice = true;
|
// 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))
|
||||||
SAFE_RELEASE(pDevices[iDevice]);
|
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:
|
SetupDiDestroyDeviceInfoList(setup_enum);
|
||||||
if (bstrNamespace)
|
return guids;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef SAFE_RELEASE
|
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <vector>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace ciface
|
namespace ciface
|
||||||
{
|
{
|
||||||
namespace DInput
|
namespace DInput
|
||||||
{
|
{
|
||||||
void GetXInputGUIDS(std::vector<DWORD>* guids);
|
std::unordered_set<DWORD> GetXInputGUIDS();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,7 @@
|
||||||
<ClCompile Include="ControllerInterface\DInput\DInput.cpp" />
|
<ClCompile Include="ControllerInterface\DInput\DInput.cpp" />
|
||||||
<ClCompile Include="ControllerInterface\DInput\DInputJoystick.cpp" />
|
<ClCompile Include="ControllerInterface\DInput\DInputJoystick.cpp" />
|
||||||
<ClCompile Include="ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
|
<ClCompile Include="ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
|
||||||
<ClCompile Include="ControllerInterface\DInput\XInputFilter.cpp">
|
<ClCompile Include="ControllerInterface\DInput\XInputFilter.cpp" />
|
||||||
<!--Disable /Zc:strictStrings so that wbem headers may compile-->
|
|
||||||
<!--
|
|
||||||
This is somewhat gross as it doesn't dynamically remove the option,
|
|
||||||
I really hope the issue is fixed in next VS release :(
|
|
||||||
-->
|
|
||||||
<AdditionalOptions>/Zo /Zc:inline /Zc:rvalueCast /volatile:iso</AdditionalOptions>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="ControllerInterface\ExpressionParser.cpp" />
|
<ClCompile Include="ControllerInterface\ExpressionParser.cpp" />
|
||||||
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
|
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
|
||||||
<ClCompile Include="ControllerInterface\XInput\XInput.cpp" />
|
<ClCompile Include="ControllerInterface\XInput\XInput.cpp" />
|
||||||
|
|
Loading…
Reference in New Issue