如何从 PreTranslateMessage(MSG*pMsg) 中的 WM_KEYDOWN 中提取字符
How to extract the character from WM_KEYDOWN in PreTranslateMessage(MSG*pMsg)
在从 CView
继承的 PreTranslateMessage(MSG *pMsg)
中的 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)
我想要 "capture" 或以不同方式处理的字符是 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`
编辑:
长话短说:U.S。键盘,SHIFT+3 = #
,而在法语键盘上 SHIFT+3 = /
。所以我不想看单个键,而是想了解字符。
在处理WM_KEYDOWN时,如何翻译lParam和wParam("keys")来找出键盘和Windows即将生成的字符?
找到神奇的 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...
}
}
不,该代码仅适用于具有显式“#”键的键盘布局。不适用于标准 U.S 这样的布局。布局,其中“#”是其他键的组合,例如 SHIFT + 3。
我相信这是一个更好的解决方案。这个是用两个标准 U.S 测试的。键盘布局和加拿大-法语键盘布局。
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()
的调用是可选的,但我发现它是我试图检测的字符所必需的。
我不是 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
}
}
假设一个U.S。用户(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 消息和 return TRUE(这样正常的 translate/dispatch 就不会发生)。您还必须小心过滤相应的 WM_KEYUP 消息,以免混淆 TranslateMessage 的内部状态。这需要大量工作和大量测试。
找个地方拦截 TranslateMessage 生成的 WM_CHAR 条消息。
对于第二个选项,您可以将目标 window 子类化,让它在字符为 '#'
时拦截 WM_CHAR 消息并传递其他所有内容。这看起来简单多了,而且针对性强。
在从 CView
继承的 PreTranslateMessage(MSG *pMsg)
中的 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)
我想要 "capture" 或以不同方式处理的字符是 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`
编辑:
长话短说:U.S。键盘,SHIFT+3 = #
,而在法语键盘上 SHIFT+3 = /
。所以我不想看单个键,而是想了解字符。
在处理WM_KEYDOWN时,如何翻译lParam和wParam("keys")来找出键盘和Windows即将生成的字符?
找到神奇的 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...
}
}
不,该代码仅适用于具有显式“#”键的键盘布局。不适用于标准 U.S 这样的布局。布局,其中“#”是其他键的组合,例如 SHIFT + 3。
我相信这是一个更好的解决方案。这个是用两个标准 U.S 测试的。键盘布局和加拿大-法语键盘布局。
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()
的调用是可选的,但我发现它是我试图检测的字符所必需的。
我不是 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
}
}
假设一个U.S。用户(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 消息和 return TRUE(这样正常的 translate/dispatch 就不会发生)。您还必须小心过滤相应的 WM_KEYUP 消息,以免混淆 TranslateMessage 的内部状态。这需要大量工作和大量测试。找个地方拦截 TranslateMessage 生成的 WM_CHAR 条消息。
对于第二个选项,您可以将目标 window 子类化,让它在字符为 '#'
时拦截 WM_CHAR 消息并传递其他所有内容。这看起来简单多了,而且针对性强。