Qaz
Qaz

Reputation: 61910

Why can I not acquire the keyboard with DirectInput?

I just started getting into DirectInput today, using DirectInput8 with MinGW on Windows 7 Ultimate N. I started off with a simple program to report which keys are currently down every second (just codes, not readable keys). However, I can't even get as far as acquiring the keyboard before it errors out:

#define _WIN32_WINNT 0x0601
#include <dinput.h>
//link to dinput8.lib and dxguid.lib

int main() {
    IDirectInput8 *dinput;
    DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);

    IDirectInputDevice8 *kb;
    dinput->CreateDevice(GUID_SysKeyboard, &kb, nullptr);

    kb->SetDataFormat(&c_dfDIKeyboard);
    kb->SetCooperativeLevel(GetConsoleWindow(), DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    kb->Acquire(); //fails with DIERR_INVALIDPARAM
}

I omitted error checking, but what happens is that every call succeeds (by judgement of the FAILED macro) up to Acquire(). That call fails with the error DIERR_INVALIDPARAM. I looked on the MSDN pages and across the web, but I can't find any reason it would fail with that based on everything before it present and working.

For good measure, I also tried looping the Acquire() call until it succeeded and then played around with windows and the keyboard while it was running, but the program never successfully acquired the keyboard in all the time it was running. How can I successfully acquire the keyboard?

Upvotes: 2

Views: 4400

Answers (3)

rauprog
rauprog

Reputation: 243

Experimenting with direct input in a console program for the keyboard and joystick

It is not necessary to have a handle to a window to use direct input. You can just use null in the parameter for a handle to a window.

Instead of:

kb->SetCooperativeLevel(GetConsoleWindow(), DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

Use:

kb->SetCooperativeLevel(NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

Here is an example program that uses direct input to display the keys 'a' and 'd' when those keys are pressed. It will repeat those keys when they are held for 300 milliseconds. The example program is also a good example of a high resolution counter. There is no need to add in properties in Visual Studio to add the libraries dinput8.lib and dxguid.lib. The #pragma comment code lines at the top of the program do that for you.

 #include <dinput.h>
#include <iostream>
#include <Windows.h>

#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")

using std::cout;
using std::endl;

LPDIRECTINPUT8 di;
LPDIRECTINPUTDEVICE8 keyboard;

class CTimer
{
public:
    CTimer() {
        QueryPerformanceFrequency(&mqFreq);
    }
    ~CTimer() {}

    void Start() {
        QueryPerformanceCounter(&mqStart);
    }
    void End() {
        QueryPerformanceCounter(&mqEnd);
    }
    double GetTimeInSeconds() {
        return (mqEnd.QuadPart - mqStart.QuadPart)/(double)mqFreq.QuadPart;
    }
    double GetTimeInMilliseconds() {
        return (1.0e3*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
    }
    double GetTimeInMicroseconds() {
        return (1.0e6*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
    }
    double GetTimeInNanoseconds() {
        return (1.0e9*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
    }

private:
    LARGE_INTEGER mqStart;
    LARGE_INTEGER mqEnd;
    LARGE_INTEGER mqFreq;
};


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

void createdikeyboard() {
    di->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
    keyboard->SetDataFormat(&c_dfDIKeyboard);
    keyboard->SetCooperativeLevel(NULL,DISCL_FOREGROUND | DISCL_EXCLUSIVE);
    keyboard->Acquire();
}

void destroydikeyboard() {
    keyboard->Unacquire();
    keyboard->Release();
}

#define keydown(name,key) (name[key] & 0x80)

void main() {
    HRESULT hr;
    BYTE dikeys[256];
    CTimer timeint;
    CTimer timekeywaspressedclass;
    double gettime;
    double lastkeywasa=false;
    double timekeywaspressed=0;
    bool lasttimetherewasakeypress=false;
    bool akeywaspressed=false;
    initializedirectinput8();
    createdikeyboard();
    hr=keyboard->GetDeviceState(256,dikeys);
    timeint.Start();
    while (!keydown(dikeys,DIK_ESCAPE)) {
        timeint.End();
        gettime=timeint.GetTimeInMilliseconds();
        if (gettime >=5) {
            hr=keyboard->GetDeviceState(256,dikeys);
            akeywaspressed=false;
            if (keydown(dikeys,DIK_A)) {
                akeywaspressed=true;
                if (timekeywaspressed >=300) {
                    cout << "a";
                    lasttimetherewasakeypress=false;
                }

            }
            if (keydown(dikeys,DIK_D)) {
                akeywaspressed=true;
                if (timekeywaspressed >=300) {
                    cout << "d";
                    lasttimetherewasakeypress=false;
                }
            }
            if (lasttimetherewasakeypress==false && akeywaspressed==true) {
                timekeywaspressedclass.Start();
                timekeywaspressed=0;
                lasttimetherewasakeypress=true;
            }
            if (lasttimetherewasakeypress==true && akeywaspressed==true) {
                timekeywaspressedclass.End();
                gettime=timekeywaspressedclass.GetTimeInMilliseconds();
                timekeywaspressed+=gettime;
                timekeywaspressedclass.Start();
            }
            if (akeywaspressed==false) {
                lasttimetherewasakeypress=false;
                timekeywaspressed=0;
            }


        } // end if (gettime >=5)

        } // end while
destroydikeyboard();

} // end main

Here is an example console program for the joystick.

#include <dinput.h>
#include <iostream>
#include <Windows.h>

#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")

using std::cout;
using std::endl;


LPDIRECTINPUT8 di;
LPDIRECTINPUTDEVICE8 joystick;
LPDIRECTINPUTDEVICE8 keyboard;
DIJOYSTATE2 js;
BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context);
BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context);

HRESULT selectjoystick() {
    HRESULT hr;
    // Look for the first simple joystick we can find.
if (FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback,
                                NULL, DIEDFL_ATTACHEDONLY))) {
    return hr;
}

// Make sure we got a joystick
if (joystick == NULL) {
  //  printf("Joystick not found.\n");
    return E_FAIL;
}
}

DIDEVCAPS capabilities;

HRESULT setjoystickproperties() {
    HRESULT hr;
    // Set the data format to "simple joystick" - a predefined data format 
//
// A data format specifies which controls on a device we are interested in,
// and how they should be reported. This tells DInput that we will be
// passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState().
if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) {
    return hr;
}

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

// Determine how many axis the joystick has (so we don't error out setting
// properties for unavailable axis)

capabilities.dwSize = sizeof(DIDEVCAPS);
if (FAILED(hr = joystick->GetCapabilities(&capabilities))) {
    return hr;
}
}

BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
    HRESULT hr;

    // Obtain an interface to the enumerated joystick.
    hr = di->CreateDevice(instance->guidInstance, &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(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 enumaxes() {
    HRESULT hr;
// Enumerate the axes of the joyctick and set the range of each axis. Note:
// we could just use the defaults, but we're just trying to show an example
// of enumerating device objects (axes, buttons, etc.).
if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) {
    return hr;
}
}

BOOL CALLBACK 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->SetProperty(DIPROP_RANGE, &propRange.diph))) {
        return DIENUM_STOP;
    }

    return DIENUM_CONTINUE;
}

HRESULT polljoy() {
    HRESULT     hr;

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


    // Poll the device to read the current state
    hr = joystick->Poll(); 
    if (FAILED(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.
        hr = joystick->Acquire();
        while (hr == DIERR_INPUTLOST) {
            hr = joystick->Acquire();
        }

        // If we encounter a fatal error, return failure.
        if ((hr == DIERR_INVALIDPARAM) || (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 (hr == DIERR_OTHERAPPHASPRIO) {
            return S_OK;
        }
    }

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

    return S_OK;
}


class CTimer
{
public:
    CTimer() {
        QueryPerformanceFrequency(&mqFreq);
    }
    ~CTimer() {}

    void Start() {
        QueryPerformanceCounter(&mqStart);
    }
    void End() {
        QueryPerformanceCounter(&mqEnd);
    }
    double GetTimeInSeconds() {
        return (mqEnd.QuadPart - mqStart.QuadPart)/(double)mqFreq.QuadPart;
    }
    double GetTimeInMilliseconds() {
        return (1.0e3*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
    }
    double GetTimeInMicroseconds() {
        return (1.0e6*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
    }
    double GetTimeInNanoseconds() {
        return (1.0e9*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
    }

private:
    LARGE_INTEGER mqStart;
    LARGE_INTEGER mqEnd;
    LARGE_INTEGER mqFreq;
};


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

void createdikeyboard() {
    di->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
    keyboard->SetDataFormat(&c_dfDIKeyboard);
    keyboard->SetCooperativeLevel(NULL,DISCL_FOREGROUND | DISCL_EXCLUSIVE);
    keyboard->Acquire();
}

void destroydikeyboard() {
    keyboard->Unacquire();
    keyboard->Release();
}

void closejoystick() {
    if (joystick) { 
    joystick->Unacquire();
    }
    if (joystick) {
        joystick->Release();
    }
}

#define keydown(name,key) (name[key] & 0x80)

void main() {
    HRESULT hr;
    CTimer pollintclass;
    hr=initializedirectinput8();
    createdikeyboard();
    hr=selectjoystick();
    hr=setjoystickproperties();
    hr=enumaxes();
    bool endloop=false;
    double gettime;
    BYTE dikeys[256];
    pollintclass.Start();
    while (endloop==false) {
        pollintclass.End();
        gettime=pollintclass.GetTimeInMilliseconds();
        if (gettime >=5) {
            hr=keyboard->GetDeviceState(256,dikeys);
            if (FAILED(hr)) 
                keyboard->Acquire();
            if (keydown(dikeys,DIK_ESCAPE)) 
                endloop=true;
            polljoy();
            cout << "joy x-axis=" << js.lX << " " << "joy y-axis=" << js.lY << endl;
            pollintclass.Start();
        }
    }
destroydikeyboard();
    closejoystick();

}

Upvotes: 2

t-mat
t-mat

Reputation: 73

Have you tried DISCL_BACKGROUND instead of DISCL_FOREGROUND ?

Upvotes: 2

Qaz
Qaz

Reputation: 61910

It actually happens that the error magically disappears when I change it from associating with the console window (which is visibly existent) to one I create in the SetCooperativeLevel() call. Why a console window can't be used I don't know, so I'll leave this unaccepted for a while in lieu of an answerer who does, but this does solve the problem.

Upvotes: 0

Related Questions