键盘输入(Win32 和 C++ 入门)
键盘用于多种不同类型的输入,包括:
- 字符输入。 用户键入到文档或编辑框中的文本。
- 键盘快捷方式。 调用应用程序函数的键笔划;例如,CTRL + O 打开文件。
- 系统命令。 调用系统函数的键笔划;例如,Alt + Tab 切换窗口。
在考虑键盘输入时,请务必记住,按键笔划与字符不同。 例如,按 A 键可能会导致以下任意字符。
- a
- A
- 如果键盘支持组合音调符号) ,则 (
此外,如果按住 Alt 键,则按 A 键将生成 Alt+A,系统根本不将其视为字符,而是将其视为系统命令。
关键代码
当你按下某个键时,硬件将生成 扫描代码。 扫描代码因键盘而异,并且有单独的扫描代码用于按键和按键按下事件。 你几乎永远不会关心扫描代码。 键盘驱动程序将扫描代码转换为 虚拟键代码。 虚拟密钥代码与设备无关。 在任何键盘上按 A 键都会生成相同的虚拟键代码。
通常,虚拟键代码不对应于 ASCII 代码或任何其他字符编码标准。 如果考虑一下,这是显而易见的,因为同一个键可以生成不同的字符 (a、A、á) ,并且某些键(如功能键)不对应于任何字符。
也就是说,以下虚拟键代码确实映射到 ASCII 等效项:
- 0 到 9 个键 = ASCII'0' – '9' (0x30 – 0x39)
- A 到 Z 键 = ASCII 'A' – 'Z' (0x41 – 0x5A)
在某些方面,这种映射是不幸的,因为出于上述原因,你不应该将虚拟键代码视为字符。
头文件 WinUser.h 为大多数虚拟键代码定义常量。 例如,向左键的虚拟键代码 VK_LEFT (0x25) 。 有关虚拟密钥代码的完整列表,请参阅 虚拟密钥代码。 没有为与 ASCII 值匹配的虚拟键代码定义常量。 例如,A 键的虚拟密钥代码0x41,但VK_A没有名为 的常量。 相反,只需使用数值。
Key-Down和Key-Up消息
当你按下某个键时,具有键盘焦点的窗口将收到以下消息之一。
WM_SYSKEYDOWN消息指示系统键,这是调用系统命令的键划。 有两种类型的系统密钥:
- Alt + 任意键
- F10
F10 键激活窗口的菜单栏。 各种 ALT 键组合调用系统命令。 例如,Alt + TAB 切换到新窗口。 此外,如果窗口有菜单,则 Alt 键可用于激活菜单项。 某些 Alt 组合键不执行任何操作。
所有其他键笔划被视为非系统键,并生成 WM_KEYDOWN 消息。 这包括 F10 以外的功能键。
释放密钥时,系统会发送相应的密钥更新消息:
如果按住一个键足够长以启动键盘的重复功能,系统会发送多个向下键消息,后跟一条向上键消息。
在到目前为止讨论的所有四条键盘消息中, wParam 参数包含键的虚拟键代码。 lParam 参数包含打包到 32 位中的一些杂项信息。 通常不需要 lParam 中的信息。 一个可能有用的标志是位 30,即“上一个键状态”标志,对于重复的按下键消息,该标志设置为 1。
顾名思义,系统键笔划主要供操作系统使用。 如果截获 WM_SYSKEYDOWN 消息,请随后调用 DefWindowProc 。 否则,将阻止操作系统处理命令。
字符消息
关键笔划由 TranslateMessage 函数转换为字符,我们在 模块 1 中首次看到该函数。 此函数检查键下消息并将其转换为字符。 对于生成的每个字符, TranslateMessage 函数将 WM_CHAR 或 WM_SYSCHAR 消息放在窗口的消息队列中。 消息的 wParam 参数包含 UTF-16 字符。
正如你可能猜到的, WM_CHAR 消息是从 WM_KEYDOWN 消息生成的,而 WM_SYSCHAR 消息是从 WM_SYSKEYDOWN 消息生成的。 例如,假设用户按 SHIFT 键后跟 A 键。 假设使用标准键盘布局,则会收到以下消息序列:
WM_KEYDOWN:SHIFT
WM_KEYDOWN:A
WM_CHAR:“A”
另一方面,ALT + P 组合将生成:
WM_SYSKEYDOWN:VK_MENU
WM_SYSKEYDOWN:0x50
WM_SYSCHAR:“p”
WM_SYSKEYUP:0x50
WM_KEYUP:VK_MENU
(由于历史原因,ALT 键的虚拟键代码VK_MENU命名。)
WM_SYSCHAR消息指示系统字符。 与 WM_SYSKEYDOWN 一样,通常应将此消息直接传递给 DefWindowProc。 否则,可能会干扰标准系统命令。 特别是,不要 将WM_SYSCHAR 视为用户键入的文本。
WM_CHAR消息是你通常认为的字符输入。 字符的数据类型为 wchar_t,表示 UTF-16 Unicode 字符。 字符输入可以包括 ASCII 范围之外的字符,尤其是常在美国之外使用的键盘布局。 可以通过安装区域键盘,然后使用屏幕键盘功能来尝试不同的键盘布局。
用户还可以安装输入法编辑器 (输入法编辑器,) 使用标准键盘输入复杂的脚本,例如日语字符。 例如,使用日语输入法输入片假名字符カ (ka) ,可能会收到以下消息:
WM_KEYDOWN:VK_PROCESSKEY (输入法进程密钥)
WM_KEYUP:0x4B
WM_KEYDOWN:VK_PROCESSKEY
WM_KEYUP:0x41
WM_KEYDOWN:VK_PROCESSKEY
WM_CHAR: カ
WM_KEYUP:VK_RETURN
某些 CTRL 组合键将转换为 ASCII 控制字符。 例如,CTRL+A 转换为 ASCII ctrl-A (SOH) 字符 (ASCII 值0x01) 。 对于文本输入,通常应筛选出控制字符。 此外,请避免使用 WM_CHAR 来实现键盘快捷方式。 请改 用WM_KEYDOWN 消息;或者更好的是,使用快捷键表。 加速键表在下一主题 加速器表中介绍。
以下代码在调试器中显示main键盘消息。 尝试使用不同的击键组合,并查看生成的消息。
注意
请务必包含 wchar.h,否则swprintf_s将未定义。
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
wchar_t msg[32];
switch (uMsg)
{
case WM_SYSKEYDOWN:
swprintf_s(msg, L"WM_SYSKEYDOWN: 0x%x\n", wParam);
OutputDebugString(msg);
break;
case WM_SYSCHAR:
swprintf_s(msg, L"WM_SYSCHAR: %c\n", (wchar_t)wParam);
OutputDebugString(msg);
break;
case WM_SYSKEYUP:
swprintf_s(msg, L"WM_SYSKEYUP: 0x%x\n", wParam);
OutputDebugString(msg);
break;
case WM_KEYDOWN:
swprintf_s(msg, L"WM_KEYDOWN: 0x%x\n", wParam);
OutputDebugString(msg);
break;
case WM_KEYUP:
swprintf_s(msg, L"WM_KEYUP: 0x%x\n", wParam);
OutputDebugString(msg);
break;
case WM_CHAR:
swprintf_s(msg, L"WM_CHAR: %c\n", (wchar_t)wParam);
OutputDebugString(msg);
break;
/* Handle other messages (not shown) */
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
其他键盘消息
大多数应用程序可以安全地忽略其他一些键盘消息。
- 为组合键(如音调符号)发送 WM_DEADCHAR 消息。 例如,在西班牙语键盘上,键入重音 (') 后跟 E 将生成字符 é。 为重音字符发送 WM_DEADCHAR 。
- WM_UNICHAR消息已过时。 它使 ANSI 程序能够接收 Unicode 字符输入。
- 当 IME 将击键序列转换为字符时,将发送 WM_IME_CHAR 字符。 除了通常的 WM_CHAR 消息之外,还会发送该消息。
键盘状态
键盘消息是事件驱动的。 也就是说,当发生一些有趣的事情(例如按键)时,你会收到一条消息,并且该消息会告诉你刚刚发生的情况。 但也可以通过调用 GetKeyState 函数随时测试密钥的状态。
例如,请考虑如何检测鼠标左键 + Alt 键的组合。 可以通过侦听键划消息并存储标志来跟踪 Alt 键的状态,但 GetKeyState 可避免麻烦。 收到 WM_LBUTTONDOWN 消息时,只需按如下所示调用 GetKeyState :
if (GetKeyState(VK_MENU) & 0x8000)
{
// ALT key is down.
}
GetKeyState 消息采用虚拟密钥代码作为输入,并返回一组位标志, (实际上只有两个标志) 。 值0x8000包含位标志,用于测试当前是否按下了键。
大多数键盘都有两个 ALT 键,左键和右键。 上一个示例测试其中任一按下。 还可以使用 GetKeyState 来区分 Alt、SHIFT 或 Ctrl 键的左实例和右键实例。 例如,以下代码测试是否按下了正确的 Alt 键。
if (GetKeyState(VK_RMENU) & 0x8000)
{
// Right ALT key is down.
}
GetKeyState 函数很有趣,因为它报告虚拟键盘状态。 此虚拟状态基于消息队列的内容,并在从队列中删除消息时更新。 当程序处理窗口消息时,GetKeyState 会在每条消息排队时提供键盘快照。 例如,如果队列上的最后一条消息 WM_LBUTTONDOWN, 则 GetKeyState 会在用户单击鼠标按钮时报告键盘状态。
由于 GetKeyState 基于消息队列,因此它还会忽略发送到另一个程序的键盘输入。 如果用户切换到另一个程序, 则 GetKeyState 会忽略发送到该程序的任何按键。 如果真的想要了解键盘的即时物理状态,有一个函数: GetAsyncKeyState。 但是,对于大多数 UI 代码,正确的函数是 GetKeyState。
下一步