在win32,WM_CHAR或WM_KEYDOWN / WM_KEYUP中处理键盘输入?

时间:2011-11-17 03:27:14

标签: c++ winapi

所以在我一直在研究的文本编辑器程序中,我使用WM_CHAR来处理来自键盘的输入。但是,我发现一些角色消息没有被记录下来。例如,如果我使用[shift] +数字键输入符号(例如%或&),则会重新录制某些符号,而其他内容如[shift] +9(导致')')则不会被录制。所以,我想知道我是否应该使用WM_KEYDOWN / WMKEYUP对来处理键盘输入。我曾经在汇编中编写了一个键盘记录器(实际上它只是我正在尝试的一个教程)并且使用了WM_KEYDOWN / WM_KEYUP对并且效果非常好。那么,我应该继续这个,还是我的程序发生了不寻常的事情?

谢谢,

Devjeet

3 个答案:

答案 0 :(得分:58)

这对你上面的评论真的是一个很长的回复,但是把它放在一个答案中,因为评论的时间太长了:)

这里要理解的核心问题是键和字符并不完全相同。一些(但不是全部)键生成字符;某些键根据移位或其他键盘状态生成不同的字符。要实现编辑器,您需要处理文本输入和非文本键盘输入,如箭头键。现在是冗长的版本,从似乎不正确的假设中脱颖而出:

 显然,Windows以非常奇怪的方式工作。 [...]当你按[shift] +9时,windows似乎在消息WM_CHAR的wParam中发送了一个VK_LEFT

听起来你可能会在这里混合两件事。使用WM_CHAR的是它为你提供了文本字符的字符代码:所以如果有人按下9键,你就会得到'9'。如果有人按下SHIFT + 9,Windows将考虑转换状态 - 你得到'('(如果使用美国键盘)。但你不会得到一个WM_CHAR用于箭头键,HOME,END等,因为它们不是文本字符。另一方面,WM_KEYDOWN不处理字符,而是处理VK_代码;所以按9给出VK_9而不管移位状态;左箭头给你VK_LEFT - 再次调整移位状态。< / p>

事情是,WM_CHAR和WM_KEYDOWN都为整个输入图片提供了两个部分 - 但你真的必须处理两个部分才能获得全部图片。并且必须要注意,在两种情况下,wParam都是一个非常不同的东西。它是WM_CHAR的字符代码,但是WM_KEYDOWN的VK_代码。不要混淆两者。

为了让事情更加混乱, VK_值与有效字符共享相同的值。打开WinUser.h(它位于编译器安装目录下的include dir中),然后查找VK_LEFT:

#define VK_LEFT           0x25

原来,0x25也是'%'字符的代码(有关详细信息,请参阅任何ascii / unicode表)。因此,如果WM_CHAR变为0x25,则表示按下shift-5(假设使用美国键盘)来创建'%';但如果WM_KEYDOWN变为0x25,则表示已按下左箭头(VK_LEFT)。而且为了增加一点混淆,AZ键和0-9键的虚拟键码恰好与'A' - 'Z'和'0' - '9'字符相同 - 这使它看起来像chars和VK_是可互换的。但它们不是:小写'a'的代码,0x61,是VK_NUMPAD1! (因此在WM_CHAR中获取0x61确实意味着'a',在WM_KEYDOWN中获取它意味着NUMPAD1。如果用户确实在未移位状态下击中'A'键,实际获得的是VK_A(与'A'相同的值)在WM_KEYDOWN中,它被转换为'a'的WM_CHAR。)

因此将所有这些结合在一起,处理键盘的典型方法是使用以下所有方法:

  • 使用WM_CHAR处理文本输入:实际文本键。 wParam是您要附加到字符串的字符,或者执行其他任何操作的字符。这将为您完成所有班次处理。

  • 使用WM_KEYDOWN处理'meta'键 - 如箭头键,主页,结尾,页面向上等。传递所有A-Z / 0-9值,默认处理将它们转换为WM_CHAR,您可以在WM_CHAR处理程序中处理它们。 (如果你想将它们用于特殊功能,你也可以在这里处理小键盘;否则它们会“落后”以数字WM_CHAR结束,具体取决于numlock状态.Windows会处理这个问题,就像它处理移位状态一样字母键。)

  • 如果你想明确处理ALT-combos(而不是使用加速器表),你可以通过WM_SYSKEYDOWN获得它们。

我认为有些键可能会出现在两者中 - Enter可能同时显示为VK_RETURN的WM_KEYDOWN和\ r或\ n \ n \ n WM_CHAR - 但我的首选是在WM_KEYDOWN中处理它,以便继续编辑键处理与文本键分开。

答案 1 :(得分:4)

Spy++会向您显示发送到窗口的消息,因此您可以试验并查看哪些消息适合您的应用。

如果安装了Visual Studio,它应该位于“开始”菜单的“程序” - >“gt;”下。 Microsoft Visual Studio - &gt; Visual Studio工具 - &gt;间谍++

答案 2 :(得分:0)

以上有用的信息启发了我创建此代码段,使您可以直观地了解从任何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;

通过修改keyState数组以使所有修改键都清晰可见,ToUnicode将始终输出您按下的未移动键。 (因此,在英语键盘上,您永远不会获得“%”,而总会获得“ 5”),只要它是人类可读的键即可。但是,您仍然必须进行VK_XXX检查才能感知箭头和其他非人类可读的键。

(我试图在我的应用程序中建立一个用户可编辑的“热键”系统,而WM_KEYXXX和WM_CHAR之间的区别让我很生气。上面的代码解决了这个问题。)