C++ 在 win32、WM_CHAR 或 WM_KEYDOWN/WM_KEYUP 中处理键盘输入?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8161741/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Handling keyboard input in win32, WM_CHAR or WM_KEYDOWN/WM_KEYUP?
提问by devjeetroy
So in the text editor program that i've been working on, I've used WM_CHAR to process input from the keyboard. However, I found that some of the character mesages are not recorded. For example, if I use [shift]+ number key to type a symbol such as % or &, some re recorded while others such as [shift]+9 (which results in ')'), are not recorded. So, I'm wondering if I should use WM_KEYDOWN/WMKEYUP pair to handle keyboard input. I once wrote a keylogger in assembly(actually it was just a tutorial that i was trying out) and had used WM_KEYDOWN/WM_KEYUP pairs and that worked out quite good. So, should I move on to this, or is it something unusual that is happening with my program?
所以在我一直在研究的文本编辑器程序中,我使用 WM_CHAR 来处理来自键盘的输入。但是,我发现有些字符信息没有被记录下来。例如,如果我使用 [shift]+ 数字键键入诸如 % 或 & 之类的符号,则会重新记录一些符号,而不会记录其他诸如 [shift]+9(导致 ')')之类的符号。所以,我想知道是否应该使用 WM_KEYDOWN/WMKEYUP 对来处理键盘输入。我曾经在汇编中编写了一个键盘记录器(实际上这只是我正在尝试的一个教程)并且使用过 WM_KEYDOWN/WM_KEYUP 对,并且效果很好。那么,我应该继续讨论这个问题,还是我的程序发生了一些不寻常的事情?
Thanks,
谢谢,
Devjeet
开发者
回答by BrendanMcK
This is really a long reply to your comment above, but putting it in an answer because it's too long for a comment :)
这确实是对您上面评论的很长的回复,但将其放入答案中,因为评论太长了:)
The core issue to understand here is that keys and characters are not quite the same thing. Some (but not all) keys generate characters; some keys generate different characters depending on shift or other keyboard state. And to implement an editor, you need to handle both textual input and also non-textual keyboard input like arrow keys. Now the long-winded version, picking off from what seems to be an incorrect assumption:
这里要理解的核心问题是键和字符并不完全相同。一些(但不是全部)键生成字符;某些键会根据 shift 或其他键盘状态生成不同的字符。并且要实现编辑器,您需要处理文本输入和非文本键盘输入(如箭头键)。现在是冗长的版本,从似乎不正确的假设中挑选出来:
Apparently, windows works in really strange ways. [...] It seems that when you press [shift]+9, windows sends a VK_LEFT in the wParam of message WM_CHAR
显然,Windows 以非常奇怪的方式工作。[...] 好像按 [shift]+9 时,windows 会在消息 WM_CHAR 的 wParam 中发送一个 VK_LEFT
Sounds like you might be mixing two things up here. The thing with WM_CHAR is that it gives you character codes for textual characters: so if someone presses the 9 key, you'll get '9'. If someone presses SHIFT+9, Windows will take the shift state into account - and you get '(' (if using US keyboard). But you won't ever get a WM_CHAR for arrow keys, HOME, END, and so on, since they are not textual characters. WM_KEYDOWN, on the other hand, does not deal in characters, but in VK_ codes; so pressing 9 gives you VK_9 regardless of shift state; and left arrow gives you VK_LEFT - again regardles of shift state.
听起来你可能在这里混淆了两件事。WM_CHAR 的问题在于它为您提供文本字符的字符代码:因此,如果有人按下 9 键,您将得到“9”。如果有人按下 SHIFT+9,Windows 会考虑到 shift 状态——你会得到 '('(如果使用美式键盘)。但是你永远不会得到 WM_CHAR 的箭头键、HOME、END 等等,因为它们不是文本字符。另一方面,WM_KEYDOWN 不处理字符,而是处理 VK_ 代码;因此,无论换档状态如何,按 9 都会为您提供 VK_9;向左箭头为您提供 VK_LEFT - 再次与换档状态无关。
The things is that WM_CHAR and WM_KEYDOWN both give you two parts to the overall input picture - but you really have to handle both to get the full picture. And have to be aware that the wParam is a very different thing in both cases. It's a character code for WM_CHAR, but a VK_ code for WM_KEYDOWN. Don't mix the two.
事情是 WM_CHAR 和 WM_KEYDOWN 都为您提供了整体输入图片的两个部分 - 但您确实必须处理这两个部分才能获得完整图片。并且必须意识到 wParam 在这两种情况下都是非常不同的。WM_CHAR 是字符代码,WM_KEYDOWN 是 VK_ 代码。不要将两者混为一谈。
And to make things more confusing, VK_ values share the same values as valid characters. Open up WinUser.h (it's in the include dir under the compiler installation dir), and look for VK_LEFT:
更令人困惑的是,VK_ 值与有效字符共享相同的值。打开WinUser.h(在编译器安装目录下的include目录下),查找VK_LEFT:
#define VK_LEFT 0x25
Turns out that 0x25 is also the code for the '%' character (see any ascii/unicode table for details). So if WM_CHAR gets 0x25, it means shift-5 was pressed (assuming US keyboard) to create a '%'; but if WM_KEYDOWN gets 0x25, it means left arrow (VK_LEFT) was pressed. And to add a bit more confusion, the Virtual Key codes for the A-Z keys and 0-9 keys happen to be the same as the 'A'-'Z' and '0'-'9' characters - which makes it seem like chars and VK_'s are interchangable. But they're not: the code for lower case 'a', 0x61, is VK_NUMPAD1! (So getting 0x61 in WM_CHAR does mean 'a', getting it in WM_KEYDOWN means NUMPAD1. And if a user does hit the 'A' key in unshifted state, what you actually get is first a VK_A (same value as 'A') in WM_KEYDOWN, which gets translated to WM_CHAR of 'a'.)
原来 0x25 也是 '%' 字符的代码(有关详细信息,请参阅任何 ascii/unicode 表)。因此,如果 WM_CHAR 获得 0x25,则表示按下了 shift-5(假设为美式键盘)以创建 '%'; 但如果 WM_KEYDOWN 得到 0x25,则表示按下了向左箭头 (VK_LEFT)。更令人困惑的是,AZ 键和 0-9 键的虚拟键代码恰好与 'A'-'Z' 和 '0'-'9' 字符相同 - 这使它看起来像字符和 VK_ 是可以互换的。但它们不是:小写 'a' 的代码,0x61,是 VK_NUMPAD1!(因此,在 WM_CHAR 中获得 0x61 确实意味着“a”,在 WM_KEYDOWN 中获得它意味着 NUMPAD1。如果用户确实在未移动状态下按下了“A”键,您实际得到的首先是一个 VK_A(与“A”相同的值)在 WM_KEYDOWN 中,
So tying all this together, the typical way to handle keyboard is to use all of the following:
因此,将所有这些结合在一起,处理键盘的典型方法是使用以下所有内容:
Use WM_CHAR to handle textual input: actual text keys. wParam is the character that you want to append to your string, or do whatever else with. This does all the shift- processing for you.
Use WM_KEYDOWN to handle 'meta' keys - like arrow keys, home, end, page up, and so on. Pass all the A-Z/0-9 values through, the default handling will turn them into WM_CHARs that you can handle in your WM_CHAR handler. (You can also handle numpad keys here if you want to use them for special functionality; otherwise they 'fall through' to end up as numeric WM_CHARs, depending on numlock state. Windows takes care of this, just as it handles shift state for the alphabetic keys.)
If you want to handle ALT- combos explicitly (rather than using an accelerator table), you'll get those via WM_SYSKEYDOWN.
使用 WM_CHAR 处理文本输入:实际文本键。wParam 是您想要附加到您的字符串的字符,或者做其他任何事情。这将为您完成所有的轮班处理。
使用 WM_KEYDOWN 来处理“元”键 - 如箭头键、home、end、page up 等。传递所有 AZ/0-9 值,默认处理会将它们转换为您可以在 WM_CHAR 处理程序中处理的 WM_CHARs。(如果您想将它们用于特殊功能,您也可以在此处处理 numpad 键;否则它们会“失败”而最终成为数字 WM_CHARs,具体取决于 numlock 状态。Windows 会处理这个问题,就像它处理字母键。)
如果您想显式地处理 ALT 组合(而不是使用加速器表),您将通过 WM_SYSKEYDOWN 获得它们。
I think there are some keys that might show up in both - Enter might show up as both a WM_KEYDOWN of VK_RETURN and as either \r or \n WM_CHAR - but my preference would be to handle it in WM_KEYDOWN, to keep editing key handling separate from text keys.
我认为有一些键可能同时出现在两者中 - Enter 可能同时显示为 VK_RETURN 的 WM_KEYDOWN 和 \r 或 \n WM_CHAR - 但我更喜欢在 WM_KEYDOWN 中处理它,以保持单独编辑键处理从文本键。
回答by Jonathon Reinhart
Spy++will show you the messages being sent to a window, so you can experiment and see what messages are appropriate for your application.
Spy++将向您显示发送到窗口的消息,因此您可以进行试验并查看哪些消息适合您的应用程序。
If you have Visual Studio installed, it should be in your Start menu, under Programs -> Microsoft Visual Studio -> Visual Studio Tools -> Spy++.
如果你安装了 Visual Studio,它应该在你的开始菜单中,在 Programs -> Microsoft Visual Studio -> Visual Studio Tools -> Spy++ 下。
回答by Mark Coniglio
The helpful message above inspired me to create this snippet, which gives you a human-readable indication of what key was pressed from any WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP independent of the state of the modifier keys.
上面有用的消息启发我创建了这个片段,它为您提供了一个人类可读的指示,表明从任何 WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP 中按下了什么键,而与修饰键的状态无关。
// get the keyboard state
BYTE keyState[256];
GetKeyboardState(keyState);
// clear all of the modifier keys so ToUnicode will ignore them
keyState[VK_CONTROL] = keyState[VK_SHIFT] = keyState[VK_MENU] = 0;
keyState[VK_LCONTROL] = keyState[VK_LSHIFT] = keyState[VK_LMENU] = 0;
keyState[VK_RCONTROL] = keyState[VK_RSHIFT] = keyState[VK_RMENU] = 0;
// convert the WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP to characters
UINT scanCode = (inLParam >> 16) & 0xFF;
int i = ToUnicode(inWParam, scanCode, keyState, outBuf, inOutBufLenCharacters, 0);
outBuf[i] = 0;
By modifying the keyState array so that all the modifier keys are clear, ToUnicode will always output the unshifted key you pressed. (So, on the English keyboard you'll never get '%' but always '5') as long as it's a human readable key. You still have to do the VK_XXX checking to sense the arrow and other non-human readable keys however.
通过修改 keyState 数组使所有修饰键都清除,ToUnicode 将始终输出您按下的未移位的键。(因此,在英文键盘上,您永远不会得到 '%',而总是得到 '5'),只要它是人类可读的键。但是,您仍然需要进行 VK_XXX 检查以感知箭头和其他非人类可读的键。
(I was trying to rig up a user editable "hot key" system in my app, and the distinction between WM_KEYXXX and WM_CHAR was making me nuts. The code above solved that problem.)
(我试图在我的应用程序中安装一个用户可编辑的“热键”系统,WM_KEYXXX 和 WM_CHAR 之间的区别让我发疯。上面的代码解决了这个问题。)