如何从PreTranslateMessage(MSG * pMsg)中的WM_KEYDOWN中提取字符

在从CViewinheritance的PreTranslateMessage(MSG *pMsg)的MFC应用程序中,我有这样的:

 if (pMsg->message == WM_KEYDOWN) ... 

这里logging了WM_KEYDOWN中的字段。 虚拟键VK_值在pMsg->wParampMsg->lParam包含几个字段,其中16-23位是键盘扫描码。

所以在我的代码中我使用:

 const int virtualKey = pMsg->wParam; const int hardwareScanCode = (pMsg->lParam >> 16) & 0x00ff; // bits 16-23 

在我的非美国键盘上,例如,当我按“#”字符时,我得到以下内容:

 virtualKey == 0xde --> VK_OEM_7 "Used for miscellaneous characters; it can vary by keyboard." hardwareScanCode == 0x29 (41 decimal) 

我想“捕捉”或处理不同的字符是ASCII“#”,0x23(35十进制)。

我的问题

如何翻译WM_KEYDOWN信息来获得我可以比较的东西,而不考虑语言或键盘布局? 我需要识别#键是否用户有一个标准的美国键盘,或不同的东西。

例如,我一直在看以下function,如:

 MapVirtualKey(virtualkey, MAPVK_VSC_TO_VK); // previous line is useless, the key VK_OEM_7 doesn't map to anything without the scan code ToAscii(virtualKey, hardwareScanCode, nullptr, &word, 0); // previous line returns zero, and zero is written to `word` 

编辑:

长话短说:在美式键盘上,SHIFT + 3 = # ,而在法式键盘上SHIFT + 3 = / 。 所以我不想看个人的钥匙,而是想知道这个angular色。

当处理WM_KEYDOWN时,如何翻译lParam和wParam(“键”)来找出键盘和Windows即将生成的字符?

我相信这是一个更好的解决方案。 这一个是与标准美国键盘布局和加拿大法国键盘布局测试。

 const int wParam = pMsg->wParam; const int lParam = pMsg->lParam; const int keyboardScanCode = (lParam >> 16) & 0x00ff; const int virtualKey = wParam; BYTE keyboardState[256]; GetKeyboardState(keyboardState); WORD ascii = 0; const int len = ToAscii(virtualKey, keyboardScanCode, keyboardState, &ascii, 0); if (len == 1 && ascii == '#') { // ...etc... } 

即使帮助页面似乎暗示keyboardState是可选的对ToAscii()的调用, ToAscii()我发现它对于我试图检测的字符是必需的。

找到了我所需要的魔术API调用: GetKeyNameText()

 if (pMsg->message == WM_KEYDOWN) { char buffer[20]; const int len = GetKeyNameTextA(pMsg->lParam, buffer, sizeof(buffer)); if (len == 1 && buffer[0] == '#') { // ...etc... } } 

不,这个代码只适用于具有明确的“#”键的键盘布局。 不适用于像“#”是其他键(如SHIFT + 3)组合的标准美式布局的布局。

我不是一个MFC专家,但这里大概是我认为它的消息循环如下所示:

 while (::GetMessage(&msg, NULL, 0, 0) > 0) { if (!app->PreTranslateMessage(&msg)) { // the hook you want to use TranslateMessage(&msg); // where WM_CHAR messages are generated DispatchMessage(&msg); // where the original message is dispatched } } 

假设一个美国用户( 3#在同一个键上)按下该键。

PreTranslateMessage钩子将看到WM_KEYDOWN消息。

如果它允许消息通过,那么TranslateMessage将生成一个WM_CHAR消息(或者来自该消息族的消息)并直接进行分发。 PreTranslateMessage将永远不会看到WM_CHAR。

无论WM_CHAR是'3'还是'#'取决于键盘的状态,特别是当前是否按下了Shift键。 但WM_KEYDOWN消息不包含所有的键盘状态。 TranslateMessage通过记录通过它的键盘消息来记录状态,因此它知道Shift(或Ctrl或Alt)是否已经关闭。

然后DispatchMessage将分派原始的WM_KEYDOWN消息。

如果你只想捕捉'#'而不是'3' ,那么你有两个选择:

  1. 让你的PreTranslateMessage挂钩跟踪所有的键盘状态(像TranslateMessage通常会这样做)。 它将不得不观察所有键盘消息来跟踪键盘状态,并使用键盘布局来判断当前消息是否会正常生成'#' 。 然后您必须手动调度WM_KEYDOWN消息并返回TRUE(这样才不会发生正常的转换/调度)。 您还必须小心地过滤相应的WM_KEYUP消息,以免混淆TranslateMessage的内部状态。 这是很多工作和很多测试。

  2. 找到一个地方来拦截TranslateMessage生成的WM_CHAR消息。

对于第二个选项,您可以继承目标窗口的子类,使其在字符'#'时拦截WM_CHAR消息,并传递其他所有内容。 这似乎更简单和有针对性。