XInput and DirectInput

XInput is an API that allows applications to receive input from the Xbox 360 Controller for Windows. This document describes the differences between XInput and DirectInput implementations of the Xbox 360 Controller and how you can support XInput devices and legacy DirectInput devices at the same time.

Ee417014.note(en-us,VS.85).gifNote

Use of legacy DirectInput is not recommended, and DirectInput is not available for Modern applications.

The New Standard: XInput

XInput is now available for game development. This is the new input standard for both the Xbox 360 and Windows XP Service Pack 1 and above, and Windows Vista. The APIs are available through the DirectX SDK and the driver is available on Windows Update and Windowsgaming.com.

There are several advantages to using XInput over DirectInput:

  • XInput is easier to use and requires less setup than DirectInput

  • Both Xbox 360 and Windows programming will use the same sets of core APIs, allowing programming to translate cross-platform much easier

  • There will be a large installed base of Xbox 360 controllers

  • XInput devices (i.e. The Xbox 360 controllers) will have vibration functionality only when using XInput APIs

  • Future controllers released for the Xbox 360 console (i.e. steering wheels) will also work on Windows

Using the Xbox 360 Controller with DirectInput

The Xbox 360 Controller is properly enumerated on DirectInput, and can be used with the DirectInput APIs. However, some functionality provided by XInput will be missing from the DirectInput implementation:

  • The left and right trigger buttons will act as a single button, not independently

  • The vibration effects will not be available

  • Querying for headset devices will not be available

The combination of the left and right triggers in DirectInput is by design. Games have always assumed that DirectInput device axes are centered when there is no user interaction with the device. However, the Xbox 360 controller was designed to register minimum value, not center, when the triggers are not being held. Older games would therefore assume user interaction.

The solution was to combine the triggers, setting one trigger to a positive direction and the other to a negative direction, so no user interaction is indicative to DirectInput of the 'control' being at center.

In order to test the trigger values separately, you must use XInput.

XInput and DirectInput Side by Side

By supporting XInput only, your game will not work with legacy DirectInput devices. XInput will not recognize these devices.

If you want your game to support legacy DirectInput devices, you may use DirectInput and XInput side by side. When enumerating your DirectInput devices, all DirectInput devices will enumerate correctly. All XInput devices will show up as both XInput and DirectInput devices, but they should not be handled through DirectInput. You will need to determine which of your Dinput devices are legacy devices, and which are XInput devices, and remove them from the enumeration of DirectInput devices.

To do this, insert this code into your DirectInput enumeration callback:

   1: #include <wbemidl.h>   
   2: #include <oleauto.h>   
   3: #include <wmsstd.h>   
   4:   
   5: //-----------------------------------------------------------------------------   
   6: // Enum each PNP device using WMI and check each device ID to see if it contains    
   7: // "IG_" (ex. "VID_045E&PID_028E&IG_00").  If it does, then it's an XInput device   
   8: // Unfortunately this information can not be found by just using DirectInput    
   9: //-----------------------------------------------------------------------------   
  10: BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )   
  11: {   
  12:     IWbemLocator*           pIWbemLocator  = NULL;   
  13:     IEnumWbemClassObject*   pEnumDevices   = NULL;   
  14:     IWbemClassObject*       pDevices[20]   = {0};   
  15:     IWbemServices*          pIWbemServices = NULL;   
  16:     BSTR                    bstrNamespace  = NULL;   
  17:     BSTR                    bstrDeviceID   = NULL;   
  18:     BSTR                    bstrClassName  = NULL;   
  19:     DWORD                   uReturned      = 0;   
  20:     bool                    bIsXinputDevice= false;   
  21:     UINT                    iDevice        = 0;   
  22:     VARIANT                 var;   
  23:     HRESULT                 hr;   
  24:   
  25:     // CoInit if needed   
  26:     hr = CoInitialize(NULL);   
  27:     bool bCleanupCOM = SUCCEEDED(hr);   
  28:   
  29:     // Create WMI   
  30:     hr = CoCreateInstance( __uuidof(WbemLocator),   
  31:                            NULL,   
  32:                            CLSCTX_INPROC_SERVER,   
  33:                            __uuidof(IWbemLocator),   
  34:                            (LPVOID*) &pIWbemLocator);   
  35:     if( FAILED(hr) || pIWbemLocator == NULL )   
  36:         goto LCleanup;   
  37:   
  38:     bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;           
  39:     bstrClassName = SysAllocString( L"Win32_PNPEntity" );   if( bstrClassName == NULL ) goto LCleanup;           
  40:     bstrDeviceID  = SysAllocString( L"DeviceID" );          if( bstrDeviceID == NULL )  goto LCleanup;           
  41:        
  42:     // Connect to WMI    
  43:     hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L,    
  44:                                        0L, NULL, NULL, &pIWbemServices );   
  45:     if( FAILED(hr) || pIWbemServices == NULL )   
  46:         goto LCleanup;   
  47:   
  48:     // Switch security level to IMPERSONATE.    
  49:     CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,    
  50:                        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );                       
  51:   
  52:     hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices );    
  53:     if( FAILED(hr) || pEnumDevices == NULL )   
  54:         goto LCleanup;   
  55:   
  56:     // Loop over all devices   
  57:     for( ;; )   
  58:     {   
  59:         // Get 20 at a time   
  60:         hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );   
  61:         if( FAILED(hr) )   
  62:             goto LCleanup;   
  63:         if( uReturned == 0 )   
  64:             break;   
  65:   
  66:         for( iDevice=0; iDevice<uReturned; iDevice++ )   
  67:         {   
  68:             // For each device, get its device ID   
  69:             hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL );   
  70:             if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )   
  71:             {   
  72:                 // Check if the device ID contains "IG_".  If it does, then it's an XInput device   
  73:                     // This information can not be found from DirectInput    
  74:                 if( wcsstr( var.bstrVal, L"IG_" ) )   
  75:                 {   
  76:                     // If it does, then get the VID/PID from var.bstrVal   
  77:                     DWORD dwPid = 0, dwVid = 0;   
  78:                     WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );   
  79:                     if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 )   
  80:                         dwVid = 0;   
  81:                     WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );   
  82:                     if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )   
  83:                         dwPid = 0;   
  84:   
  85:                     // Compare the VID/PID to the DInput device   
  86:                     DWORD dwVidPid = MAKELONG( dwVid, dwPid );   
  87:                     if( dwVidPid == pGuidProductFromDirectInput->Data1 )   
  88:                     {   
  89:                         bIsXinputDevice = true;   
  90:                         goto LCleanup;   
  91:                     }   
  92:                 }   
  93:             }      
  94:             SAFE_RELEASE( pDevices[iDevice] );   
  95:         }   
  96:     }   
  97:   
  98: LCleanup:   
  99:     if(bstrNamespace)   
 100:         SysFreeString(bstrNamespace);   
 101:     if(bstrDeviceID)   
 102:         SysFreeString(bstrDeviceID);   
 103:     if(bstrClassName)   
 104:         SysFreeString(bstrClassName);   
 105:     for( iDevice=0; iDevice<20; iDevice++ )   
 106:         SAFE_RELEASE( pDevices[iDevice] );   
 107:     SAFE_RELEASE( pEnumDevices );   
 108:     SAFE_RELEASE( pIWbemLocator );   
 109:     SAFE_RELEASE( pIWbemServices );   
 110:   
 111:     if( bCleanupCOM )   
 112:         CoUninitialize();   
 113:   
 114:     return bIsXinputDevice;   
 115: }   
 116:   
 117:   
 118: //-----------------------------------------------------------------------------   
 119: // Name: EnumJoysticksCallback()   
 120: // Desc: Called once for each enumerated joystick. If we find one, create a   
 121: //       device interface on it so we can play with it.   
 122: //-----------------------------------------------------------------------------   
 123: BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,   
 124:                                      VOID* pContext )   
 125: {   
 126:     HRESULT hr;   
 127:   
 128:     if( IsXInputDevice( &pdidInstance->guidProduct ) )   
 129:         return DIENUM_CONTINUE;   
 130:   
 131:      // Device is verified not XInput, so add it to the list of DInput devices   
 132:   
 133:      return DIENUM_CONTINUE;       
 134: }  
 135:  
 136: #include <wbemidl.h>
 137: #include <oleauto.h>
 138: #include <wmsstd.h>
 139:  
 140: //-----------------------------------------------------------------------------
 141: // Enum each PNP device using WMI and check each device ID to see if it contains 
 142: // "IG_" (ex. "VID_045E&PID_028E&IG_00").  If it does, then it's an XInput device
 143: // Unfortunately this information can not be found by just using DirectInput 
 144: //-----------------------------------------------------------------------------
 145: BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
 146: {
 147:     IWbemLocator*           pIWbemLocator  = NULL;
 148:     IEnumWbemClassObject*   pEnumDevices   = NULL;
 149:     IWbemClassObject*       pDevices[20]   = {0};
 150:     IWbemServices*          pIWbemServices = NULL;
 151:     BSTR                    bstrNamespace  = NULL;
 152:     BSTR                    bstrDeviceID   = NULL;
 153:     BSTR                    bstrClassName  = NULL;
 154:     DWORD                   uReturned      = 0;
 155:     bool                    bIsXinputDevice= false;
 156:     UINT                    iDevice        = 0;
 157:     VARIANT                 var;
 158:     HRESULT                 hr;
 159:  
 160:     // CoInit if needed
 161:     hr = CoInitialize(NULL);
 162:     bool bCleanupCOM = SUCCEEDED(hr);
 163:  
 164:     // Create WMI
 165:     hr = CoCreateInstance( __uuidof(WbemLocator),
 166:                            NULL,
 167:                            CLSCTX_INPROC_SERVER,
 168:                            __uuidof(IWbemLocator),
 169:                            (LPVOID*) &pIWbemLocator);
 170:     if( FAILED(hr) || pIWbemLocator == NULL )
 171:         goto LCleanup;
 172:  
 173:     bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;        
 174:     bstrClassName = SysAllocString( L"Win32_PNPEntity" );   if( bstrClassName == NULL ) goto LCleanup;        
 175:     bstrDeviceID  = SysAllocString( L"DeviceID" );          if( bstrDeviceID == NULL )  goto LCleanup;        
 176:     
 177:     // Connect to WMI 
 178:     hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 
 179:                                        0L, NULL, NULL, &pIWbemServices );
 180:     if( FAILED(hr) || pIWbemServices == NULL )
 181:         goto LCleanup;
 182:  
 183:     // Switch security level to IMPERSONATE. 
 184:     CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, 
 185:                        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );                    
 186:  
 187:     hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); 
 188:     if( FAILED(hr) || pEnumDevices == NULL )
 189:         goto LCleanup;
 190:  
 191:     // Loop over all devices
 192:     for( ;; )
 193:     {
 194:         // Get 20 at a time
 195:         hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );
 196:         if( FAILED(hr) )
 197:             goto LCleanup;
 198:         if( uReturned == 0 )
 199:             break;
 200:  
 201:         for( iDevice=0; iDevice<uReturned; iDevice++ )
 202:         {
 203:             // For each device, get its device ID
 204:             hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL );
 205:             if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )
 206:             {
 207:                 // Check if the device ID contains "IG_".  If it does, then it's an XInput device
 208:                     // This information can not be found from DirectInput 
 209:                 if( wcsstr( var.bstrVal, L"IG_" ) )
 210:                 {
 211:                     // If it does, then get the VID/PID from var.bstrVal
 212:                     DWORD dwPid = 0, dwVid = 0;
 213:                     WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );
 214:                     if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 )
 215:                         dwVid = 0;
 216:                     WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );
 217:                     if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )
 218:                         dwPid = 0;
 219:  
 220:                     // Compare the VID/PID to the DInput device
 221:                     DWORD dwVidPid = MAKELONG( dwVid, dwPid );
 222:                     if( dwVidPid == pGuidProductFromDirectInput->Data1 )
 223:                     {
 224:                         bIsXinputDevice = true;
 225:                         goto LCleanup;
 226:                     }
 227:                 }
 228:             }   
 229:             SAFE_RELEASE( pDevices[iDevice] );
 230:         }
 231:     }
 232:  
 233: LCleanup:
 234:     if(bstrNamespace)
 235:         SysFreeString(bstrNamespace);
 236:     if(bstrDeviceID)
 237:         SysFreeString(bstrDeviceID);
 238:     if(bstrClassName)
 239:         SysFreeString(bstrClassName);
 240:     for( iDevice=0; iDevice<20; iDevice++ )
 241:         SAFE_RELEASE( pDevices[iDevice] );
 242:     SAFE_RELEASE( pEnumDevices );
 243:     SAFE_RELEASE( pIWbemLocator );
 244:     SAFE_RELEASE( pIWbemServices );
 245:  
 246:     if( bCleanupCOM )
 247:         CoUninitialize();
 248:  
 249:     return bIsXinputDevice;
 250: }
 251:  
 252:  
 253: //-----------------------------------------------------------------------------
 254: // Name: EnumJoysticksCallback()
 255: // Desc: Called once for each enumerated joystick. If we find one, create a
 256: //       device interface on it so we can play with it.
 257: //-----------------------------------------------------------------------------
 258: BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
 259:                                      VOID* pContext )
 260: {
 261:     HRESULT hr;
 262:  
 263:     if( IsXInputDevice( &pdidInstance->guidProduct ) )
 264:         return DIENUM_CONTINUE;
 265:  
 266:      // Device is verified not XInput, so add it to the list of DInput devices
 267:  
 268:      return DIENUM_CONTINUE;    
 269: }
posted @ 2012-02-06 15:22  wangjijian  阅读(620)  评论(0编辑  收藏  举报