原文链接:https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx
XInput是一个允许应用从Windows平台Xbox 360控制器中接收输入的API。该文档描述XInput和DirectInput关于Xbox 360控制器接口间的不同,并说明如何在同一时间同时支持XInput设备和传统DirectInput设备。
注意:传统DirectInput目前不推荐使用,并且在Windows Store里面的应用已经不支持DirectInput。
新的标准:XInput
XInput目前可以应用于游戏开发,这个新的输入标准可以应用于Xbox 360、Windows XP SP1及以上版本以及Windows Vista。该APIs在DirectX SDK中包含,并且通过Windows Update可以更新驱动。
XInput相较于DirectInput有以下方面的优势:
- XInput比DirectInput更方便使用并且创建步骤更少
- Xbox 360和Windows编程使用相同的核心APIs集,并且跨平台编程更方便
- Xbox 360有一个非常大的用户群
- XInput设备(也就是指Xbox 360控制器)将在仅使用XInput APIs时具备震动功能
- 未来发布关于Xbox 360操纵台的控制器(也就是方向盘)将同样可以应用于Windows
将DirectInput应用于Xbox 360控制器
Xbox 360可以通过DirectInput进行枚举,并且可以应用DirectInput APIs。然而,某些XInput提供的功能在DirectInput中不存在:
- 左、右扳柄不作为独立的单一按钮
- 震动效果不可用
- 无法查询头戴式设备
在DirectInput中设计左右扳柄处于组合状态,游戏常常假定当没有用户与设备交互时DirectInput设备轴处于中间。然而,Xbox 360控制器设计该位置为最小值,并不是中间,当触发器没有被控制时,老的游戏因此假定用户处于交互状态。
解决方案是配置扳柄,设置一个为负向和另一个为正向,所以当没有用户交互时表示DirectInput控制处于中间状态。
为了分别测试扳柄的值,此时必须用XInput。
同时支持XInput和DirectInput
假如仅仅支持XInput,游戏将不能工作在传统的DirectInput设备上。XInput将不会识别这些设备。
如何想让游戏支持传统DirectInput设备,需要同时使用DirectInput和XInput。当枚举DirectInput设备时,所有DirectInput设备将枚举成功。所有的XInput设备将同时显示在XInput和DirectInput设备中,但是他们不应该通过DirectInput处理。你需要决定那些DirectInput设备是传统设备,那些是XInput设备并且从DirectInput设备枚举集里面将他们移除。
为实现这个目的,将下面这些代码插入到DirectInput回调函数里面:
#include <wbemidl.h> #include <oleauto.h> #include <wmsstd.h> //----------------------------------------------------------------------------- // Enum each PNP device using WMI and check each device ID to see if it contains // "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device // Unfortunately this information can not be found by just using DirectInput //----------------------------------------------------------------------------- BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput ) { IWbemLocator* pIWbemLocator = NULL; IEnumWbemClassObject* pEnumDevices = NULL; IWbemClassObject* pDevices[20] = {0}; IWbemServices* pIWbemServices = NULL; BSTR bstrNamespace = NULL; BSTR bstrDeviceID = NULL; BSTR bstrClassName = NULL; DWORD uReturned = 0; bool bIsXinputDevice= false; UINT iDevice = 0; VARIANT var; HRESULT hr; // CoInit if needed hr = CoInitialize(NULL); bool bCleanupCOM = SUCCEEDED(hr); // Create WMI hr = CoCreateInstance( __uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*) &pIWbemLocator); if( FAILED(hr) || pIWbemLocator == NULL ) goto LCleanup; bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; // Connect to WMI hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices ); if( FAILED(hr) || pIWbemServices == NULL ) goto LCleanup; // Switch security level to IMPERSONATE. CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); if( FAILED(hr) || pEnumDevices == NULL ) goto LCleanup; // Loop over all devices for( ;; ) { // Get 20 at a time hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned ); if( FAILED(hr) ) goto LCleanup; if( uReturned == 0 ) break; for( iDevice=0; iDevice<uReturned; iDevice++ ) { // For each device, get its device ID hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL ); if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL ) { // 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 DWORD dwVidPid = MAKELONG( dwVid, dwPid ); if( dwVidPid == pGuidProductFromDirectInput->Data1 ) { bIsXinputDevice = true; goto LCleanup; } } } SAFE_RELEASE( pDevices[iDevice] ); } } LCleanup: if(bstrNamespace) SysFreeString(bstrNamespace); if(bstrDeviceID) SysFreeString(bstrDeviceID); if(bstrClassName) SysFreeString(bstrClassName); for( iDevice=0; iDevice<20; 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 ) { HRESULT hr; if( IsXInputDevice( &pdidInstance->guidProduct ) ) return DIENUM_CONTINUE; // Device is verified not XInput, so add it to the list of DInput devices return DIENUM_CONTINUE; }