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;
+  static const GUID s_GUID_devclass_HID = {
+      0x745a17a0, 0x74d3, 0x11d0, {0xb6, 0xfe, 0x00, 0xa0, 0xc9, 0x0f, 0x57, 0xda}};
+  std::unordered_set<DWORD> guids;
 
-  // CoInit if needed
-  hr = CoInitialize(nullptr);
-  bool bCleanupCOM = SUCCEEDED(hr);
+  // 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;
 
-  // 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)
+  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();
 }
 }
diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj
index 3e63243949..3ffd5c8397 100644
--- a/Source/Core/InputCommon/InputCommon.vcxproj
+++ b/Source/Core/InputCommon/InputCommon.vcxproj
@@ -41,14 +41,7 @@
     <ClCompile Include="ControllerInterface\DInput\DInput.cpp" />
     <ClCompile Include="ControllerInterface\DInput\DInputJoystick.cpp" />
     <ClCompile Include="ControllerInterface\DInput\DInputKeyboardMouse.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\DInput\XInputFilter.cpp" />
     <ClCompile Include="ControllerInterface\ExpressionParser.cpp" />
     <ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
     <ClCompile Include="ControllerInterface\XInput\XInput.cpp" />