在PreTranslateMessage(MSG *pMsg)
继承的CView
内的MFC应用程序中,我有:
if (pMsg->message == WM_KEYDOWN) ...
WM_KEYDOWN
中的字段已记录here。虚拟键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
信息以获取可与之比较的内容,无论语言或键盘布局如何?我需要识别#
密钥,用户是否有标准的美式键盘,或者不同的东西。
例如,我一直在研究以下功能,例如:
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 = /
。所以我不想看单个键,而是想知道这个角色。
处理WM_KEYDOWN时,如何翻译lParam和wParam(“键”)以找出键盘和Windows即将生成的字符?
答案 0 :(得分:4)
我相信这是一个更好的解决方案。这个测试使用标准的美国键盘布局和加拿大 - 法国键盘布局进行测试。
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()
的调用是可选的,但我发现需要使用我试图检测到的字符。
答案 1 :(得分:0)
找到魔法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等其他键的组合。
答案 2 :(得分:0)
我不是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消息并传递其他所有内容。这似乎更简单,目标明确。