Why isn't my DirectX program recognizing that I have issued a escape key? (C ++)

EDIT: after modifying the code even more, the error still exists, the modified code is displayed:

KeyDown()

:

const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
    return (keys[key] & input_bit_num) != 0;
}

      

PollKeyboard()

:

LPDIRECTINPUTDEVICE8 di_keyboard;
void PollKeyboard()
{
    long result = di_keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);
    char para[16];
    itoa(result, para, 17);
        if(result != DI_OK) MessageBox(NULL, para, "ERROR", MB_OK);

}

      

When I try to put a MessageBox in an KeyDown()

if statement (as shown below in a game loop), the MessageBox just pops up even if I stop pressing the key, that is: I click, "Do you want to quit? A message appears, I say no, it disappears. and then reappears again as if I still hold the key.

This is my loop:

void GameRun(HWND hWnd) //called once every frame
{
    PollKeyboard();
    if(GetTickCount - start >= 30)
    {
        if(KeyDown(DIK_LEFT))
            MoveLeft();
        if(KeyDown(DIK_RIGHT))
            MoveRight();
    }

    if(d3ddev->BeginScene())
    {
        //rendering
    }

    if(KeyDown(DIK_ESCAPE))
    {
        //any MessageBox()
        int result = MessageBox(hWnd, "I'm causing so much trouble!", "IMMORTAL", MB_YESNOCANCEL);
        if(result == IDYES)
            //end
    }
}

      

EDIT: catch in PollKeyboard()

displays the sequence 53gd6bcc

, however I couldn't find the error code that matches.

EDIT: After another test, I saw that even though the MessageBox is not included in the KeyDown () if statement, the crash still occurs.

EDIT: After a bit more testing, it seems that the MessageBox itself is causing the crash.

+2


source to share


2 answers


Since the sample code works, something else in your program is throwing an error. Try moving the bit of code below into your own until it works, then you know which section of code was the culprit.

Sample code

Ok, a huge block of code fits. This code is working correctly for me. (Escape as well as all other keys are successfully activated and deactivated). He is great, commented and explains everything quite well. Try it, if it works, we'll look at other parts of your program, if not, I can only leave you with "Luck" and take what you want:

// DirectInput
#define DIRECTINPUT_VERSION 0x0800
#include<dinput.h>

// Standard stuff
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>

// Link from code, MSVC specific, could be done in project settings
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")

// Utility lexical_cast, use Boost if possible.
// Simple replacement, converts a stream-able `T`
// to a string
template <typename T>
const std::string lexical_cast(const T& pValue)
{
    std::stringstream ss;
    ss << pValue;

    return ss.str();
}

// Utility function + macro to execute DirectX code with exceptions.
// Kinda ugly, but helpful for us.
void check_error(HRESULT pResult, const std::string& pFuncName)
{
    // DI_OK == S_OK, but S_OK is more general, so we'll use that
    if (pResult != S_OK)
    {
        throw std::runtime_error("Error executing: " + pFuncName +
                                "! Returned: " + lexical_cast(pResult));
    }
}

// Macro, makes calling the function easier. It is wrapped in
// an `if` statement for reasons outlined in:
// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.5
#define CHECK_ERROR(x) if (true) { check_error(x, #x); } else (void)0

// The above gives the warning:
// "warning C4127: conditional expression is constant", disable below:
#pragma warning(disable: 4127)

// Manages input
class input_manager
{
public:
    // Constants
    static const int NumberKeys = 256;

    // Creation
    input_manager(void)
    {
        // Create input and keyboard (like I said, ugly macro, but helpful :] )
        CHECK_ERROR(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
            IID_IDirectInput8, reinterpret_cast<void**>(&_input), 0));


        CHECK_ERROR(_input->CreateDevice(GUID_SysKeyboard, &_keyboard, 0));
        CHECK_ERROR(_keyboard->SetDataFormat(&c_dfDIKeyboard));
        CHECK_ERROR(_keyboard->Acquire());
    }

    ~input_manager(void)
    {
        // Free resources. Note: Many programmers
        // unnecessarily wrap this stuff in
        // `if (_keyboard !=0)`, and then
        // `_keyboard = 0`. This is completely unnecessary,
        // because destructors are only run one time.

        // Also, I can assume they are valid, because if they
        // weren't, we wouldn't be here (an exception would have
        // been thrown)

        _keyboard->Unacquire();
        _keyboard->Release();
        _input->Release();

        // Also, if we wrapped this into a nice RAII class, we wouldn't
        // be forced to write a destructor, but this is outside the scope.
        // Feel free to ask how; additionally, since we're on the topic, if you'd
        // like more tips handling input (I've written PLENTY of input managers)
        // I'm free for asking about things like testing for triggers rather than pressed
        // ("was it pressed, regardless if it being held now" versus
        // "is it being pressed"), etc.
    }

    // Operations
    void update(void)
    {
        CHECK_ERROR(_keyboard->GetDeviceState(NumberKeys, reinterpret_cast<void*>(&_keys)));
    }

    // Query
    bool key_pressed(int pKey) const
    {
        return test_key(pKey);
    }

    // Might wrap into an operator[] for convenience.

private:
    // Constants
    static const int PressMask = 0x80;

    // Sorry for the confusion, but indeed, with
    // `char`s the mask is simply 0x80.

    // Utility
    bool test_key(int pKey) const
    {
        return (_keys[pKey] & PressMask) != 0;
    }

    // Members
    LPDIRECTINPUT8 _input;
    LPDIRECTINPUTDEVICE8 _keyboard;

    char _keys[NumberKeys];
};

void test_keys(const input_manager& input)
{
    bool anyPressed = false;

    for (unsigned i = 0; i < input_manager::NumberKeys; ++i)
    {
        if (input.key_pressed(i))
        {
            std::cout << "Pressing: " << i << std::endl;

            anyPressed = true;
        }
    }

    if (!anyPressed)
    {
        std::cout << "No keys pressed." << std::endl;
    }
}

void execute(void)
{
    input_manager input;

    std::cout << "Press Q to quit." << std::endl;

    bool running = true;
    while (running)
    {
        input.update();

        if (input.key_pressed(DIK_Q))
        {
            running = false;
        }

        test_keys(input);

        Sleep(0); // give some processor time
    }
}

int main(void)
{
    // Place real code in an execute function, so main
    // is clean and ready to catch exceptions:
    try
    {
        execute();
    }
    catch (const std::exception& e)
    {
        // Error!
        std::cerr << "Unhandled exception:" << e.what() << std::endl;
    }
}

      

Previous sentence:

Try to catch the return value from GetDeviceState:

HRESULT result =                              // v Prefer C++-style casts
    di_keyboard->GetDeviceState(sizeof(keys), reinterpret_cast<void*>(&keys);

if (result != DI_OK)
{
    // uh-oh
    std::cout << result << std::endl;
}

      

Compare this to the table here .

Old semi-answer:

Shortly after editing the code in the Extra Stuff section, I realized the error, sorry I didn't catch it sooner. You are testing the wrong bit :)

Note:

//                                     v HERE! Should be 0x8000, not 0x80.
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;

      



Try the following:

int KeyDown(int key)
{
    return (keys[key] & 0x8000);
}

      

Also, this should be moved to a constant to avoid magic numbers:

// somewhere, probably in the private section of the class or in a detail namespace:
static const int PushedMask = 0x8000;

// code reads better:
int KeyDown(int key)
{
    return (keys[key] & PushedMask);
}

      

Finally, in C ++ you have a type bool

, so use that!

// v here
bool KeyDown(int key)
{
    return (keys[key] & PushedMask);
}

      

I know Visual Studio will warn about this conversion from int

to bool

, so you can get rid of it and also make your intentions clearer:

bool KeyDown(int key)
{
    return (keys[key] & PushedMask) != 0; // or == 1, your choice
}

      

Additional materials:

Try using the following code:

#include <iostream>
#include <windows.h>

bool key_pressed(int pKeyCode)
{
    return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
}

void test_keys(void)
{
    for (unsigned i = 0; i < 255; ++i)
    {
        if (key_pressed(i))
        {
            std::cout << "Pressing: " << i << std::endl;
        }
    }
}

int main(void)
{
    bool running = true;
    while (running)
    {
        if (key_pressed(VK_ESCAPE))
        {
            running = false;
        }

        test_keys();

        Sleep(0);
    }
}

      

This works for me (responds to all keys, exits on exit). A minimal test case for GetAsyncKeyState

. If that doesn't work, add OS, keyboard, etc. In your comment.

+2


source


If you create a MessageBox (Null, ...), after creating it, you will have no control over the window. IE, the window won't disappear when you press a key.

As for why it keeps popping up, it seems to have something to do with this:

const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
    return (keys[key] & input_bit_num) != 0;
}

      



contains 1 byte characters and input_bit_num contains 2 bytes. Although I honestly don't know which bit is what you are looking for (0xff - 0x00 is 1 byte domain).

To be honest, I'm surprised your code works, unless the operation and operation is carried over to the [key-1] keys, in which case any KeyDown will be undefined, and KeyDown (...) when the key is 0 is especially dangerous ...

+1


source







All Articles