Vergelijking van XInput- en DirectInput-functies
Belangrijk
Zie GameInput API- voor meer informatie over de invoer-API van de volgende generatie die wordt ondersteund op pc en Xbox via de Microsoft Game Development Kit (GDK).
Dit document vergelijkt XInput en DirectInput implementaties van controllerinvoer en hoe u zowel XInput-apparaten als verouderde DirectInput-apparaten ondersteunt.
Windows Store-apps bieden geen ondersteuning voor DirectInput-.
Overzicht
Met XInput kunnen toepassingen invoer ontvangen van de XUSB-controllers. De API's zijn beschikbaar via de DirectX SDK en het stuurprogramma is beschikbaar via Windows Update.
Het gebruik van XInput heeft verschillende voordelen ten opzichte van DirectInput-:
- XInput is eenvoudiger te gebruiken en vereist minder installatie dan DirectInput-
- Zowel Xbox- als Windows-programmering maakt gebruik van dezelfde sets kern-API's, waardoor programmeren veel eenvoudiger kan worden vertaald op meerdere platforms
- Er is een grote geïnstalleerde basis van controllers
- XInput-apparaat heeft alleen trillingsfunctionaliteit bij het gebruik van XInput-API's
XUSB-controllers gebruiken met DirectInput
De XUSB-controllers worden correct geïnventariseerd op DirectInputen kunnen worden gebruikt met de DirectInputAPIs. Sommige functionaliteit van XInput ontbreekt echter in de DirectInput-implementatie:
- De linker- en rechtertriggerknoppen fungeren als één knop, niet onafhankelijk
- De trillingseffecten zijn niet beschikbaar
- Query's uitvoeren op headsetapparaten is niet beschikbaar
De combinatie van de linker- en rechtertriggers in DirectInput- is standaard. Games hebben altijd aangenomen dat directInput-apparaatassen zijn gecentreerd wanneer er geen gebruikersinteractie met het apparaat is. De nieuwere controllers zijn echter ontworpen om de minimumwaarde te registreren, niet in het midden, wanneer de triggers niet worden vastgehouden. Oudere games gaan daarom uit van gebruikersinteractie.
De oplossing was om de triggers te combineren, één trigger in te stellen op een positieve richting en de andere op een negatieve richting, dus er is geen tussenkomst van de gebruiker indicatief voor DirectInput- van het 'besturingselement' dat zich in het midden bevindt.
Als u de triggerwaarden afzonderlijk wilt testen, moet u XInput gebruiken.
XInput en DirectInput naast elkaar
Door alleen XInput te ondersteunen, werkt uw game niet met verouderde DirectInput apparaten. XInput herkent deze apparaten niet.
Als u wilt dat uw game verouderde DirectInput apparaten ondersteunt, kunt u DirectInput en XInput naast elkaar gebruiken. Bij het inventariseren van uw DirectInput-apparaten worden alle DirectInput-apparaten correct opgesomd. Alle XInput-apparaten worden weergegeven als zowel XInput- als DirectInput-apparaten, maar ze moeten niet worden verwerkt via DirectInput. U moet bepalen welke van uw DirectInput-apparaten verouderde apparaten zijn en welke XInput-apparaten zijn en deze verwijderen uit de opsomming van DirectInput-apparaten.
Hiervoor voegt u deze code in uw DirectInput-inventarisatie-callback in:
#include <wbemidl.h>
#include <oleauto.h>
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } }
#endif
//-----------------------------------------------------------------------------
// Enum each PNP device using WMI and check each device ID to see if it contains
// "IG_" (ex. "VID_0000&PID_0000&IG_00"). If it does, then it's an XInput device
// Unfortunately this information cannot be found by just using DirectInput
//-----------------------------------------------------------------------------
BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
{
IWbemLocator* pIWbemLocator = nullptr;
IEnumWbemClassObject* pEnumDevices = nullptr;
IWbemClassObject* pDevices[20] = {};
IWbemServices* pIWbemServices = nullptr;
BSTR bstrNamespace = nullptr;
BSTR bstrDeviceID = nullptr;
BSTR bstrClassName = nullptr;
bool bIsXinputDevice = false;
// CoInit if needed
HRESULT hr = CoInitialize(nullptr);
bool bCleanupCOM = SUCCEEDED(hr);
// So we can call VariantClear() later, even if we never had a successful IWbemClassObject::Get().
VARIANT var = {};
VariantInit(&var);
// 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.
hr = CoSetProxyBlanket(pIWbemServices,
RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
nullptr, EOAC_NONE);
if ( FAILED(hr) )
goto LCleanup;
hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, nullptr, &pEnumDevices);
if (FAILED(hr) || pEnumDevices == nullptr)
goto LCleanup;
// Loop over all devices
for (;;)
{
ULONG uReturned = 0;
hr = pEnumDevices->Next(10000, _countof(pDevices), pDevices, &uReturned);
if (FAILED(hr))
goto LCleanup;
if (uReturned == 0)
break;
for (size_t iDevice = 0; iDevice < uReturned; ++iDevice)
{
// 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 cannot 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_s(strVid, L"VID_%4X", &dwVid) != 1)
dwVid = 0;
WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
if (strPid && swscanf_s(strPid, L"PID_%4X", &dwPid) != 1)
dwPid = 0;
// Compare the VID/PID to the DInput device
DWORD dwVidPid = MAKELONG(dwVid, dwPid);
if (dwVidPid == pGuidProductFromDirectInput->Data1)
{
bIsXinputDevice = true;
goto LCleanup;
}
}
}
VariantClear(&var);
SAFE_RELEASE(pDevices[iDevice]);
}
}
LCleanup:
VariantClear(&var);
if(bstrNamespace)
SysFreeString(bstrNamespace);
if(bstrDeviceID)
SysFreeString(bstrDeviceID);
if(bstrClassName)
SysFreeString(bstrClassName);
for (size_t iDevice = 0; iDevice < _countof(pDevices); ++iDevice)
SAFE_RELEASE(pDevices[iDevice]);
SAFE_RELEASE(pEnumDevices);
SAFE_RELEASE(pIWbemLocator);
SAFE_RELEASE(pIWbemServices);
if(bCleanupCOM)
CoUninitialize();
return bIsXinputDevice;
}
//-----------------------------------------------------------------------------
// Name: EnumJoysticksCallback()
// Desc: Called once for each enumerated joystick. If we find one, create a
// device interface on it so we can play with it.
//-----------------------------------------------------------------------------
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
VOID* pContext )
{
if( IsXInputDevice( &pdidInstance->guidProduct ) )
return DIENUM_CONTINUE;
// Device is verified not XInput, so add it to the list of DInput devices
return DIENUM_CONTINUE;
}
Een enigszins verbeterde versie van deze code bevindt zich in de verouderde DirectInput Joystick voorbeeld.