在从CView
inheritance的PreTranslateMessage(MSG *pMsg)
的MFC应用程序中,我有这样的:
if (pMsg->message == WM_KEYDOWN) ...
这里logging了WM_KEYDOWN
中的字段。 虚拟键VK_
值在pMsg->wParam
而pMsg->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'
,那么你有两个选择:
让你的PreTranslateMessage挂钩跟踪所有的键盘状态(像TranslateMessage通常会这样做)。 它将不得不观察所有键盘消息来跟踪键盘状态,并使用键盘布局来判断当前消息是否会正常生成'#'
。 然后您必须手动调度WM_KEYDOWN消息并返回TRUE(这样才不会发生正常的转换/调度)。 您还必须小心地过滤相应的WM_KEYUP消息,以免混淆TranslateMessage的内部状态。 这是很多工作和很多测试。
找到一个地方来拦截TranslateMessage生成的WM_CHAR消息。
对于第二个选项,您可以继承目标窗口的子类,使其在字符'#'
时拦截WM_CHAR消息,并传递其他所有内容。 这似乎更简单和有针对性。