为啥我的 DirectX 程序无法识别我已经释放了转义键? (C++)
Posted
技术标签:
【中文标题】为啥我的 DirectX 程序无法识别我已经释放了转义键? (C++)【英文标题】:Why doesn't my DirectX program recognize that I've released the escape key? (C++)为什么我的 DirectX 程序无法识别我已经释放了转义键? (C++) 【发布时间】:2009-10-19 20:43:41 【问题描述】:编辑:经过更多代码修改后,错误仍然存在,显示修改后的代码:
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);
当我尝试将 MessageBox 放入 KeyDown()
if 语句(如下面的游戏循环中所示)时,即使我停止按键,MessageBox 仍会继续出现,即:我按下 ,“你想要退出?”消息框出现,我说不,它消失了,然后立即重新出现,好像我还在握着钥匙。
这是我的循环:
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
编辑:PollKeyboard()
中的 catch 显示序列 53gd6bcc
,但是我找不到它对应的错误代码。
编辑:经过另一次测试,我看到即使 MessageBox 不在 KeyDown() if 语句中,故障仍然发生。
编辑:经过更多测试,似乎是 MessageBox 本身导致了故障。
【问题讨论】:
您实际上是通过KeyDown
然后再次调用PollKeyboard
,对吗?现实检查:]
我们可以看看你的循环吗?您发布的代码看起来不错。
请注意,您实际上并没有使用常量 :)
哎呀,我在编辑代码时忘记更改它:)
那么当您释放密钥时,您是否希望消息框消失?如果是这样,您将不会有任何运气。 MessageBox 不能以这种方式工作。
【参考方案1】:
由于示例代码有效,因此您的程序中的其他问题导致了该错误。尝试将下面的代码部分移动到你自己的代码中,直到它起作用,然后你就会知道哪一段代码是罪魁祸首。
示例代码
好的,巨大的代码块即将出现。这段代码对我来说是正确的。 (Escape,以及所有其他键成功激活和停用)。它很大,有评论,并且很好地解释了事情。试试这个,如果可行,我们会检查你程序的其他部分,如果不行,我只能给你“祝你好运”,然后拿走你想要的:
// 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's 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;
老建议:
尝试从 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;
将其与table here 进行比较。
旧的半答案:
在编辑 Extra Stuff 部分的代码后不久,我意识到了错误,抱歉我没有早点发现它。您正在测试错误的位:)
观察:
// v HERE! Should be 0x8000, not 0x80.
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
试试看:
int KeyDown(int key)
return (keys[key] & 0x8000);
此外,应该将其移入常量以避免幻数:
// 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);
最后,在 C++ 中你有一个 bool
类型,所以好好利用它吧!
// v here
bool KeyDown(int key)
return (keys[key] & PushedMask);
我知道 Visual Studio 会警告这种从 int
到 bool
的转换,因此您可以摆脱它,同时让您的意图更清晰:
bool KeyDown(int key)
return (keys[key] & PushedMask) != 0; // or == 1, your choice
额外的东西:
试试下面的代码:
#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);
这对我有用(响应所有键,退出时退出)。 GetAsyncKeyState
的最小测试用例。如果这样做不起作用,请在您的评论中添加操作系统、键盘等。
【讨论】:
GetAsyncKeyState 根本不起作用(即使使用 VK_LEFT 和 RIGHT) :哦!一些非常奇怪的事情正在发生。让我写一个小测试程序。 是的,它可以工作,事实证明只是0x80 / 0x8000
阻止了GatAsyncKeyState()
工作,而且,当使用GetAsyncKeyState()
时,故障不会出现,所以它在直接输入。
它确实有效,就像它检测按键一样,但故障(我现在知道只发生在 MessageBox() 中,消息框继续出现)仍然存在,所以我仍然需要解决这个问题。
对。好吧,检查返回值,虽然我怀疑它工作正常。我要对 DirectInput 做一个最小的测试,看看它是否适合我,然后让你测试它。如果它有效,则错误超出了您的输入例程。如果不是,则解决方案超出了知识范围。【参考方案2】:
如果您创建 MessageBox(Null,...) 在创建后您将无法控制窗口。 IE,按下键时窗口不会消失。
至于为什么一直出现,似乎与此有关:
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
return (keys[key] & input_bit_num) != 0;
keys 由 1 字节长的字符组成,而 input_bit_num 是 2 字节的值。虽然我真的不知道您要查找的是哪一位(0xff - 0x00 是 1 字节的域)。
老实说,我对您的代码运行感到惊讶,除非 & 操作延续到 keys[key-1] 在这种情况下,任何 KeyDown 都是未定义的,并且 KeyDown(...) 当 key 为 0 时特别危险.
【讨论】:
以上是关于为啥我的 DirectX 程序无法识别我已经释放了转义键? (C++)的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 @Aspect 无法被我的 SpringBoot 应用程序识别?
为啥 Netbeans 无法识别我的 Grails 应用程序中的 Maven 依赖项导入?