How do I prevent non connected Joystick from crashing my DirectInput API based application?

352 Views Asked by At

The Joysticks works as expected except that if it's not plugged my app crashes. The interesting thing, I used it on the computer that I make the development in, it does not crash when not plugged, only on other computers. So that's something to keep in mind

The way I used the API is as follow :

I setup global variables

LPDIRECTINPUTDEVICE8 JoystickDriver::joystick;
LPDIRECTINPUT8 JoystickDriver::di;
DIDEVCAPS JoystickDriver::capabilities;
HRESULT JoystickDriver::hr;
DIJOYSTATE2 JoystickDriver::jstate;

In the callback function that is executed at a fixed time step

Main Callback :

if (*isInitialized != 1 )
{
    js->initialize();
    js->selectJoystick();
    js->setProperties();
    js->enumAxes();     
    *isInitialized = 1;     
}

if (js->PollData(&Joystick::jstate) == S_OK)
{
    slider[0] = (real_T)(1000 - Joystick::jstate.rglSlider[0]) / 20;

    pov[0] = (real_T)Joystick::jstate.lX;
    pov[1] = (real_T)Joystick::jstate.lY;
    pov[2] = (real_T)Joystick::jstate.lRz;

    Buttons[0] = (uint8_T)(Joystick::jstate.rgbButtons[0] & 0x80);
    Buttons[1] = (uint8_T)(Joystick::jstate.rgbButtons[1] & 0x80);
    Buttons[2] = (uint8_T)(Joystick::jstate.rgbButtons[2] & 0x80);
    Buttons[3] = (uint8_T)(Joystick::jstate.rgbButtons[3] & 0x80);
    Buttons[4] = (uint8_T)(Joystick::jstate.rgbButtons[4] & 0x80);

}

In the Class definition/implementation

Joystick.h



class Joystick
{
public:
    Joystick();
    ~Joystick();

    HRESULT initialize();
    HRESULT selectJoystick();
    static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context);
    HRESULT setProperties();
    HRESULT enumAxes();
    static BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context);
    HRESULT PollData(DIJOYSTATE2 *js);
    void disConnectJoystick();

    static LPDIRECTINPUTDEVICE8 joystick;
    static LPDIRECTINPUT8 di;
    static DIDEVCAPS capabilities;
    static HRESULT hr;
    static DIJOYSTATE2 jstate;
};

Joystick.cpp

#include "Joystick.h"


Joystick::Joystick()
{
}


Joystick::~Joystick()
{
}


HRESULT Joystick::initialize()
{
    // Create a DirectInput device
    if (FAILED(Joystick::hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
        IID_IDirectInput8, (VOID**)&(Joystick::di), NULL)))
    {
        return Joystick::hr;
    }
    else
        return E_FAIL;
}


HRESULT Joystick::selectJoystick()
{
    // Look for the first simple joystick we can find.
    if (FAILED(Joystick::hr = this->di->EnumDevices(DI8DEVCLASS_GAMECTRL, &Joystick::enumCallback, NULL, DIEDFL_ATTACHEDONLY)))
    {
        return Joystick::hr;
    }
    // Make sure we got a joystick
    if (Joystick::joystick == NULL) {
        printf("Joystick not found.\n");
        return E_FAIL;
    }
    return Joystick::hr;
}



BOOL CALLBACK Joystick::enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{

    // Obtain an interface to the enumerated joystick.
    Joystick::hr = (Joystick::di)->CreateDevice(instance->guidInstance, &Joystick::joystick, NULL);

    // If it failed, then we can't use this joystick. (Maybe the user unplugged
    // it while we were in the middle of enumerating it.)
    if (FAILED(Joystick::hr)) {
        return DIENUM_CONTINUE;
    }

    // Stop enumeration. Note: we're just taking the first joystick we get. You
    // could store all the enumerated joysticks and let the user pick.
    return DIENUM_STOP;
}


HRESULT Joystick::setProperties()
{
    if (FAILED(Joystick::hr = Joystick::joystick->SetDataFormat(&c_dfDIJoystick2)))
    {
        return Joystick::hr;
    }

    // Set the cooperative level to let DInput know how this device should
    // interact with the system and with other DInput applications.
    if (FAILED(Joystick::hr = Joystick::joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND)))
    {
        return Joystick::hr;
    }

    // Determine how many axis the joystick has (so we don't error out setting
    // properties for unavailable axis)
    Joystick::capabilities.dwSize = sizeof(DIDEVCAPS);
    if (FAILED(Joystick::hr = Joystick::joystick->GetCapabilities(&Joystick::capabilities)))
    {
        return Joystick::hr;
    }
    return E_FAIL;
}


HRESULT Joystick::enumAxes()
{
    if (FAILED(Joystick::hr = Joystick::joystick->EnumObjects(Joystick::enumAxesCallback, NULL, DIDFT_AXIS)))
    {
        return Joystick::hr;
    }
    else
        return Joystick::hr;
}


BOOL CALLBACK Joystick::enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context)
{
    HWND hDlg = (HWND)context;

    DIPROPRANGE propRange;
    propRange.diph.dwSize = sizeof(DIPROPRANGE);
    propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    propRange.diph.dwHow = DIPH_BYID;
    propRange.diph.dwObj = instance->dwType;
    propRange.lMin = -1000;
    propRange.lMax = +1000;

    // Set the range for the axis
    if (FAILED(Joystick::joystick->SetProperty(DIPROP_RANGE, &propRange.diph)))
    {
        return DIENUM_STOP;
    }

    return DIENUM_CONTINUE;
}

HRESULT Joystick::PollData(DIJOYSTATE2 *js)
{

    if (Joystick::joystick == NULL) {
        return S_OK;
    }


    // Poll the device to read the current state
    Joystick::hr = Joystick::joystick->Poll();
    if (FAILED(Joystick::hr))
    {
        // DInput is telling us that the input stream has been
        // interrupted. We aren't tracking any state between polls, so
        // we don't have any special reset that needs to be done. We
        // just re-acquire and try again.
        Joystick::hr = Joystick::joystick->Acquire();
        while (Joystick::hr == DIERR_INPUTLOST)
        {
            Joystick::hr = Joystick::joystick->Acquire();
        }

        // If we encounter a fatal error, return failure.
        if ((Joystick::hr == DIERR_INVALIDPARAM) || (Joystick::hr == DIERR_NOTINITIALIZED))
        {
            return E_FAIL;
        }

        // If another application has control of this device, return successfully.
        // We'll just have to wait our turn to use the joystick.
        if (Joystick::hr == DIERR_OTHERAPPHASPRIO)
        {
            return S_OK;
        }
    }

    // Get the input's device state
    if (FAILED(Joystick::hr = Joystick::joystick->GetDeviceState(sizeof(DIJOYSTATE2), js)))
    {
        return Joystick::hr; // The device should have been acquired during the Poll()
    }

    return S_OK;
}


void Joystick::disConnectJoystick()
{
    if (Joystick::joystick)
    {
        Joystick::joystick->Unacquire();
    }
}
0

There are 0 best solutions below