Step 2: Creating the DirectInput Force Feedback Device

In order to have DirectInput enumerate devices, you must create a callback function of the same type as DIEnumDevicesProc. In Step 1 you passed the address of this function to the IDirectInput::EnumDevices method.

DirectInput passes into the callback, as the first parameter, a pointer to a DIDEVICEINSTANCE structure that tells you what you need to know about the device. The structure member of chief interest in the example is guidInstance, the unique identifier for the particular piece of hardware on the user's system You will need to pass this GUID to the IDirectInput::CreateDevice method.

Here's the first part of the callback, which extracts the GUID and creates the device object:

BOOL CALLBACK DIEnumDevicesProc(LPCDIDEVICEINSTANCE lpddi, 
                                LPVOID pvRef)
  {
  HRESULT hr1, hr2;
  LPDIRECTINPUTDEVICE lpdidGame;
  GUID DeviceGuid = lpddi->guidInstance;
 
  // create game device
 
  hr1 = lpdi->CreateDevice(DeviceGuid, &lpdidGame, NULL); 
 

Note that the pointer to the IDirectInputDevice object, lpdidGame, is a local variable. You're not going to keep it, because in order to create force feedback effects you need to obtain a pointer to the IDirectInputDevice2 interface, as follows:

if (SUCCEEDED(hr1)) 
    { 
    hr2 = lpdidGame->QueryInterface(IID_IDirectInputDevice2, 
                                   (void **) &g_lpdid2Game); 
    lpdidGame->Release();
    } 
  else
    {
    OutputDebugString("Failed to create device.\n");
    return DIENUM_STOP;
    } 
  if (FAILED(hr2))
    {
    OutputDebugString("Failed to obtain interface.\n");
    return DIENUM_STOP;
    } 
 

The next steps, still within the callback function, are similar to those for setting up any input device. Note that you need the exclusive cooperative level for any force feedback device. Since the joystick will be used for input as well as force feedback, you also need to set the data format.

// set cooperative level
  if (FAILED(g_lpdid2Game->SetCooperativeLevel(hMainWindow,
         DISCL_EXCLUSIVE | DISCL_FOREGROUND)))
    {
    OutputDebugString(
      "Failed to set cooperative level.\n");
    lpdid2Game->Release();
    lpdi2Game = NULL;
    return DIENUM_STOP;
    }
 
  // set game data format
  if (FAILED(g_lpdid2Game->SetDataFormat(&c_dfDIJoystick)))
    {
    OutputDebugString("Failed to set game device data format.\n");
    lpdid2Game->Release();
    lpdid2Game = NULL;
    return DIENUM_STOP;
    }
 

Finally. you may want to turn off the device's autocenter feature. Autocenter is essentially a condition effect that uses the motors to simulate the springs in a standard joystick. Turning it off gives you more control over the device.

DIPROPDWORD DIPropAutoCenter;
 
  DIPropAutoCenter.diph.dwSize = sizeof(DIPropAutoCenter);
  DIPropAutoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  DIPropAutoCenter.diph.dwObj = 0;
  DIPropAutoCenter.diph.dwHow = DIPH_DEVICE;
  DIPropAutoCenter.dwData = 0;

  if (FAILED(lpdid2Game->SetProperty(DIPROP_AUTOCENTER, 
                                       &DIPropAutoCenter.diph)))
    {
    OutputDebugString("Failed to change device property.\n");
    }

  return DIENUM_STOP;   // One is enough.
  } // end DIEnumDevicesProc
 

Before using the device, you must acquire it. See Step 5: Gaining Access to the Joystick in the previous tutorial for an example of how to handle acquisition.