应用程序应接受键盘和鼠标的用户输入。 应用程序以发布到窗口的消息形式接收键盘输入。
键盘输入模型
系统通过安装适用于当前键盘的键盘设备驱动程序,为应用程序提供与设备无关的键盘支持。 系统通过使用用户或应用程序当前选择的语言特定的键盘布局提供与语言无关的键盘支持。 键盘设备驱动程序从键盘接收扫描代码,这些代码将发送到键盘布局,这些代码将转换为消息,并发布到应用程序中的相应窗口。
分配给键盘上每个键的唯一值称为 扫描代码(键盘上键的设备依赖标识符)。 当用户按下一个键时,键盘会生成两个扫描代码:一个是用户按下一个键时,另一个是用户释放键时。
键盘设备驱动程序解释扫描代码并将其转换为 虚拟键代码,这是系统定义的独立于设备的值,用于标识键用途。 转换扫描代码后,键盘布局将创建一条消息,其中包含扫描代码、虚拟键代码以及有关击键的其他信息,然后将消息置于系统消息队列中。 系统从系统消息队列中删除消息,并将其发布到相应线程的消息队列。 最终,线程的消息循环会删除该消息,并将其传递给相应的窗口过程进行处理。 下图演示了键盘输入模型。
键盘焦点和激活
系统将键盘消息发布到创建具有键盘焦点的窗口的前景线程的消息队列。 键盘焦点 是窗口的临时属性。 系统通过将键盘焦点从一个窗口转移到另一个窗口,在显示器上的所有窗口之间共享键盘。 具有键盘焦点的窗口接收(从创建它的线程的消息队列)所有键盘消息,直到焦点更改为其他窗口。
线程可以调用 GetFocus 函数来确定其窗口(如果有)当前具有键盘焦点。 线程可以通过调用 SetFocus 函数,为其中一个窗口提供键盘焦点。 当键盘焦点从一个窗口更改为另一个窗口时,系统将 WM_KILLFOCUS 消息发送到失去焦点的窗口,然后将 WM_SETFOCUS 消息发送到获得焦点的窗口。
键盘焦点的概念与活动窗口的概念相关。 活动窗口 是用户当前正在使用的顶级窗口。 具有键盘焦点的窗口是活动窗口或活动窗口的子窗口。 为了帮助用户识别活动窗口,系统将它放置在 Z 订单的顶部,并突出显示其标题栏(如果有)和边框。
用户可以通过单击该窗口、使用 alt+Tab 或 Alt+Esc 组合键或从任务列表中选择它来激活顶级窗口。 线程可以使用 SetActiveWindow 函数激活顶级窗口。 它可以使用 GetActiveWindow 函数来确定创建的顶级窗口是否处于活动状态。
停用一个窗口并激活另一个窗口时,系统将发送 WM_ACTIVATE 消息。 如果正在停用窗口,则 wParam 参数的低序字为零,如果正在激活窗口,则为非零。 当默认窗口过程收到 WM_ACTIVATE 消息时,它将键盘焦点设置为活动窗口。
若要阻止键盘和鼠标输入事件到达应用程序,请使用 BlockInput。 请注意,BlockInput 函数不会干扰异步键盘输入状态表。 这意味着在阻止输入时调用 SendInput 函数将更改异步键盘输入状态表。
击键消息
按下键会使 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息被放入附加到具有键盘焦点的窗口的线程消息队列中。 释放密钥会导致将 WM_KEYUP 或 WM_SYSKEYUP 消息置于队列中。
通常情况下,按键和抬键消息是成对出现的,但如果用户按住一个键足够长时间以启动键盘的自动重复功能,系统将连续生成大量WM_KEYDOWN 或 WM_SYSKEYDOWN 消息。 然后,当用户释放密钥时,它会生成单个 WM_KEYUP 或 WM_SYSKEYUP 消息。
本部分介绍以下主题:
系统和非系统击键
系统区分系统击键和非系统击键。 系统击键生成系统击键消息,WM_SYSKEYDOWN 和 WM_SYSKEYUP。 非系统击键生成非系统击键消息,WM_KEYDOWN 和 WM_KEYUP。
如果窗口过程必须处理系统击键消息,请确保在处理消息后,该过程会将其传递给 DefWindowProc 函数。 否则,每当窗口具有键盘焦点时,将禁用所有涉及 Alt 键的系统操作。 也就是说,用户无法访问窗口的菜单或系统菜单,或使用 Alt+Esc 或 Alt+Tab 击键激活其他窗口。
系统击键消息主要供系统使用,而不是由应用程序使用。 系统使用它们向菜单提供其内置键盘界面,并允许用户控制哪个窗口处于活动状态。 当用户键入一个键并同时键入 Alt 键时,或者当用户键入且没有窗口具有键盘焦点时(例如,活动应用程序最小化时),将生成系统击键消息。 在这种情况下,消息将发布到附加到活动窗口的消息队列。
非系统击键消息供应用程序窗口使用;DefWindowProc 函数不对其执行任何操作。 窗口过程可以放弃不需要的任何非系统击键消息。
描述的虚拟键代码
击键消息的 wParam 参数包含按下或释放的键的 虚拟键代码。 窗口过程根据虚拟键代码的值处理或忽略击键消息。
典型的窗口过程只处理一小部分它接收到的击键消息,而忽略其余的消息。 例如,窗口过程可能只处理 WM_KEYDOWN 击键消息,并且只有那些包含光标移动键、移位键(也称为控制键)和函数键的虚拟键代码。 典型的窗口过程不处理字符键中的击键消息。 相反,它使用 TranslateMessage 函数将消息转换为字符消息。 有关 TranslateMessage 和字符消息的详细信息,请参阅 字符消息。
击键消息标志
击键消息的 lParam 参数包含有关生成消息的击键的其他信息。 此信息包括 重复计数、扫描代码、扩展键标志、上下文代码、以前的键状态标志以及 转换状态标志。 下图显示了 lParam 参数中这些标志和值的位置。
应用程序可以使用以下值从 lParam 的高位字中获取击键标志。
价值 | 描述 |
---|---|
KF_EXTENDED 0x0100 |
操作扩展键标志。 |
KF_DLGMODE 0x0800 |
操作对话框模式标志,该标志指示对话框是否处于活动状态。 |
KF_MENUMODE 0x1000 |
操控菜单模式标志,该标志指示菜单是否处于活动状态。 |
KF_ALTDOWN 0x2000 |
操作上下文代码标志。 |
KF_REPEAT 0x4000 |
操作上一个键状态标志。 |
KF_UP 0x8000 |
操作转换状态标志。 |
示例代码:
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
{
WORD vkCode = LOWORD(wParam); // virtual-key code
WORD keyFlags = HIWORD(lParam);
WORD scanCode = LOBYTE(keyFlags); // scan code
BOOL isExtendedKey = (keyFlags & KF_EXTENDED) == KF_EXTENDED; // extended-key flag, 1 if scancode has 0xE0 prefix
if (isExtendedKey)
scanCode = MAKEWORD(scanCode, 0xE0);
BOOL wasKeyDown = (keyFlags & KF_REPEAT) == KF_REPEAT; // previous key-state flag, 1 on autorepeat
WORD repeatCount = LOWORD(lParam); // repeat count, > 0 if several keydown messages was combined into one message
BOOL isKeyReleased = (keyFlags & KF_UP) == KF_UP; // transition-state flag, 1 on keyup
// if we want to distinguish these keys:
switch (vkCode)
{
case VK_SHIFT: // converts to VK_LSHIFT or VK_RSHIFT
case VK_CONTROL: // converts to VK_LCONTROL or VK_RCONTROL
case VK_MENU: // converts to VK_LMENU or VK_RMENU
vkCode = LOWORD(MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX));
break;
}
// ...
}
break;
重复计数
可以检查重复计数,以确定击键消息是否表示多个击键。 当键盘生成 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息的速度比应用程序可以更快地处理消息时,系统会递增计数。 当用户按住足够长的时间来启动键盘的自动重复功能时,通常会发生这种情况。 系统不会将生成的按键消息填充到系统消息队列中,而是将消息组合成单个按键消息并增加重复计数。 释放密钥无法启动自动重复功能,因此 WM_KEYUP 和 WM_SYSKEYUP 消息的重复计数始终设置为 1。
扫描二维码
扫描代码是用户在按下键时系统生成的值。 它是一个值,该值标识按下的键(不考虑活动键盘布局),而不是由键表示的字符。 应用程序通常忽略扫描代码。 而是使用虚拟键代码来解释击键消息。
新式键盘使用 人机界面设备(HID) 规范来与计算机通信。 键盘驱动程序 转换从键盘发送的 HID 使用情况值以扫描代码并将其传递给应用程序。
注意
虽然虚拟键码通常对桌面应用程序更有用,但在某些情况下,当你需要知道在不考虑当前 键盘布局的情况下按下的是哪个键时,可能需要使用扫描码。 例如,WASD(W 向上,A 向左,S 向下,D 向右)游戏按键绑定,这可确保 US QWERTY 或 法式 AZERTY 键盘布局中按键排列的一致性。
下表列出了 Windows 当前识别的扫描代码集。 HID 使用页面/HID 使用 ID/HID 使用名称值引用 HID 使用表文档。 键位置 值引用前面的键盘图像。
Scan 1 Make 代码在 WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP 和 WM_INPUT 消息中传递。
HID 使用情况页名称 | HID 用法名称 | HID 使用情况页 | HID 用法 ID | Scan 1 Make | 密钥位置 |
---|---|---|---|---|---|
通用桌面 | 系统关机 | 0x0001 | 0x0081 | 0xE05E | |
通用桌面 | 系统睡眠 | 0x0001 | 0x0082 | 0xE05F | |
通用桌面 | 系统唤醒 | 0x0001 | 0x0083 | 0xE063 | |
键盘/小键盘 | ErrorRollOver | 0x0007 | 0x0001 | 0x00FF | |
键盘/小键盘 | 键盘 A | 0x0007 | 0x0004 | 0x001E | 31 |
键盘/小键盘 | 键盘 B | 0x0007 | 0x0005 | 0x0030 | 50 |
键盘/小键盘 | 键盘 C | 0x0007 | 0x0006 | 0x002E | 48 |
键盘/数字键盘 | 键盘 D | 0x0007 | 0x0007 | 0x0020 | 33 |
键盘/小键盘 | 键盘 E | 0x0007 | 0x0008 | 0x0012 | 19 |
键盘/小键盘 | 键盘 F | 0x0007 | 0x0009 | 0x0021 | 34 |
键盘/小键盘 | 键盘 G | 0x0007 | 0x000A | 0x0022 | 35 |
键盘/小键盘 | 键盘 H | 0x0007 | 0x000B | 0x0023 | 36 |
键盘/小键盘 | 键盘 I | 0x0007 | 0x000C | 0x0017 | 24 |
键盘/小键盘 | 键盘 J | 0x0007 | 0x000D | 0x0024 | 37 |
键盘/小键盘 | 键盘 K | 0x0007 | 0x000E | 0x0025 | 38 |
键盘/小键盘 | 键盘 L | 0x0007 | 0x000F | 0x0026 | 39 |
键盘/小键盘 | 键盘 M | 0x0007 | 0x0010 | 0x0032 | 52 |
键盘/小键盘 | 键盘 N | 0x0007 | 0x0011 | 0x0031 | 51 |
键盘/数字键盘 | 键盘 O | 0x0007 | 0x0012 | 0x0018 | 25 |
键盘/小键盘 | 键盘 P | 0x0007 | 0x0013 | 0x0019 | 26 |
键盘/小键盘 | 键盘 Q | 0x0007 | 0x0014 | 0x0010 | 17 |
键盘/小键盘 | 键盘 R | 0x0007 | 0x0015 | 0x0013 | 20 |
键盘/小键盘 | 键盘 S | 0x0007 | 0x0016 | 0x001F | 32 |
键盘/小键盘 | 键盘 T | 0x0007 | 0x0017 | 0x0014 | 21 |
键盘/小键盘 | 键盘 U | 0x0007 | 0x0018 | 0x0016 | 23 |
键盘/小键盘 | 键盘 V | 0x0007 | 0x0019 | 0x002F | 49 |
键盘/小键盘 | 键盘 W | 0x0007 | 0x001A | 0x0011 | 18 |
键盘/数字键盘 | 键盘 X | 0x0007 | 0x001B | 0x002D | 47 |
键盘/数字键盘 | 键盘 Y | 0x0007 | 0x001C | 0x0015 | 22 |
键盘/小键盘 | 键盘 Z | 0x0007 | 0x001D | 0x002C | 46 |
键盘/小键盘 | 键盘 1 和 ! | 0x0007 | 0x001E | 0x0002 | 2 |
键盘/小键盘 | 键盘 2 和 @ | 0x0007 | 0x001F | 0x0003 | 3 |
键盘/小键盘 | 键盘 3 和哈希 | 0x0007 | 0x0020 | 0x0004 | 4 |
键盘/小键盘 | 键盘 4 和 $ | 0x0007 | 0x0021 | 0x0005 | 5 |
键盘/小键盘 | 键盘 5 和 % | 0x0007 | 0x0022 | 0x0006 | 6 |
键盘/小键盘 | 键盘 6 和 ^ | 0x0007 | 0x0023 | 0x0007 | 7 |
键盘/小键盘 | 键盘 7 和 & | 0x0007 | 0x0024 | 0x0008 | 8 |
键盘/小键盘 | 键盘 8 和 * | 0x0007 | 0x0025 | 0x0009 | 9 |
键盘/小键盘 | 键盘 9 和 ( | 0x0007 | 0x0026 | 0x000A | 10 |
键盘/小键盘 | 键盘 0 和右括号 | 0x0007 | 0x0027 | 0x000B | 11 |
键盘/小键盘 | 键盘回车 Enter 键 | 0x0007 | 0x0028 | 0x001C | 43 |
键盘/数字键盘 | 键盘 Escape 键 | 0x0007 | 0x0029 | 0x0001 | 110 |
键盘/小键盘 | 键盘 Delete 键 | 0x0007 | 0x002A | 0x000E | 15 |
键盘/小键盘 | 键盘选项卡 | 0x0007 | 0x002B | 0x000F | 16 |
键盘/小键盘 | 键盘空格键 | 0x0007 | 0x002C | 0x0039 | 61 |
键盘/小键盘 | 键盘 - 和 _ | 0x0007 | 0x002D | 0x000C | 12 |
键盘/小键盘 | 键盘 = 和 + | 0x0007 | 0x002E | 0x000D | 13 |
键盘/小键盘 | 键盘 { | 0x0007 | 0x002F | 0x001A | 27 |
键盘/小键盘 | 键盘 } | 0x0007 | 0x0030 | 0x001B | 28 |
键盘/小键盘 | 键盘 | 和 \ | 0x0007 | 0x0031 | 0x002B | 29 |
键盘/小键盘 | 键盘非美国 | 0x0007 | 0x0032 | 0x002B | 42 |
键盘/小键盘 | 键盘 ; 和 : | 0x0007 | 0x0033 | 0x0027 | 40 |
键盘/小键盘 | 键盘撇号和双引号 | 0x0007 | 0x0034 | 0x0028 | 41 |
键盘/小键盘 | 键盘 ` 和 ~ | 0x0007 | 0x0035 | 0x0029 | 1 |
键盘/小键盘 | 键盘逗号 | 0x0007 | 0x0036 | 0x0033 | 53 |
键盘/小键盘 | 键盘 . | 0x0007 | 0x0037 | 0x0034 | 54 |
键盘/小键盘 | 键盘 ? | 0x0007 | 0x0038 | 0x0035 | 55 |
键盘/小键盘 | 键盘盖锁 | 0x0007 | 0x0039 | 0x003A | 30 |
键盘/键区 | 键盘 F1 | 0x0007 | 0x003A | 0x003B | 112 |
键盘/小键盘 | 键盘 F2 | 0x0007 | 0x003B | 0x003C | 113 |
键盘/小键盘 | 键盘 F3 | 0x0007 | 0x003C | 0x003D | 114 |
键盘/小键盘 | 键盘 F4 | 0x0007 | 0x003D | 0x003E | 115 |
键盘/键区 | 键盘 F5 | 0x0007 | 0x003E | 0x003F | 116 |
键盘/小键盘 | 键盘 F6 | 0x0007 | 0x003F | 0x0040 | 117 |
键盘/小键盘 | 键盘 F7 | 0x0007 | 0x0040 | 0x0041 | 118 |
键盘/小键盘 | 键盘 F8 | 0x0007 | 0x0041 | 0x0042 | 119 |
键盘/小键盘 | 键盘 F9 | 0x0007 | 0x0042 | 0x0043 | 120 |
键盘/小键盘 | 键盘 F10 | 0x0007 | 0x0043 | 0x0044 | 121 |
键盘/小键盘 | 键盘 F11 | 0x0007 | 0x0044 | 0x0057 | 122 |
键盘/键区 | 键盘 F12 | 0x0007 | 0x0045 | 0x0058 | 123 |
键盘/小键盘 | 键盘 PrintScreen 键 | 0x0007 | 0x0046 | 0xE037 0x0054 *注意 1 |
124 |
键盘/小键盘 | 键盘滚动锁 | 0x0007 | 0x0047 | 0x0046 | 125 |
键盘/小键盘 | 键盘暂停 | 0x0007 | 0x0048 | 0xE11D45 0xE046 *注释 2 0x0045 *注释 3 |
126 |
键盘/小键盘 | 键盘 Insert 键 | 0x0007 | 0x0049 | 0xE052 | 75 |
键盘/小键盘 | 键盘主页 | 0x0007 | 0x004A | 0xE047 | 80 |
键盘/小键盘 | 键盘 PageUp 键 | 0x0007 | 0x004B | 0xE049 | 85 |
键盘/小键盘 | 键盘 Delete Forward 键 | 0x0007 | 0x004C | 0xE053 | 76 |
键盘/小键盘 | 键盘 End 键 | 0x0007 | 0x004D | 0xE04F | 81 |
键盘/数字键盘 | 键盘 PageDown 键 | 0x0007 | 0x004E | 0xE051 | 86 |
键盘/小键盘 | 键盘向右键 | 0x0007 | 0x004F | 0xE04D | 89 |
键盘/数字键盘 | 键盘向左键 | 0x0007 | 0x0050 | 0xE04B | 79 |
键盘/小键盘 | 键盘向下键 | 0x0007 | 0x0051 | 0xE050 | 84 |
键盘/小键盘 | 键盘向上键 | 0x0007 | 0x0052 | 0xE048 | 83 |
键盘/小键盘 | 小键盘 Num Lock 和清除键 | 0x0007 | 0x0053 | 0x0045 0xE045 *注释 3 |
90 |
键盘/小键盘 | 小键盘 / | 0x0007 | 0x0054 | 0xE035 | 95 |
键盘/小键盘 | 小键盘 * | 0x0007 | 0x0055 | 0x0037 | 100 |
键盘/小键盘 | 小键盘 - | 0x0007 | 0x0056 | 0x004A | 105 |
键盘/小键盘 | 小键盘 + | 0x0007 | 0x0057 | 0x004E | 106 |
键盘/小键盘 | 键盘 ENTER | 0x0007 | 0x0058 | 0xE01C | 108 |
键盘/小键盘 | 小键盘 1 和 End | 0x0007 | 0x0059 | 0x004F | 93 |
键盘/小键盘 | 小键盘 2 和向下键 | 0x0007 | 0x005A | 0x0050 | 98 |
键盘/小键盘 | 小键盘 3 和 PageDn | 0x0007 | 0x005B | 0x0051 | 103 |
键盘/小键盘 | 小键盘 4 和向左键 | 0x0007 | 0x005C | 0x004B | 92 |
键盘/小键盘 | 键盘 5 | 0x0007 | 0x005D | 0x004C | 97 |
键盘/小键盘 | 小键盘 6 和向左键 | 0x0007 | 0x005E | 0x004D | 102 |
键盘/小键盘 | 小键盘 7 和 Home | 0x0007 | 0x005F | 0x0047 | 91 |
键盘/小键盘 | 小键盘 8 和向上键 | 0x0007 | 0x0060 | 0x0048 | 96 |
键盘/小键盘 | 小键盘 9 和 PageUp | 0x0007 | 0x0061 | 0x0049 | 101 |
键盘/小键盘 | 小键盘 0 和 Insert | 0x0007 | 0x0062 | 0x0052 | 99 |
键盘/小键盘 | 小键盘 . | 0x0007 | 0x0063 | 0x0053 | 104 |
键盘/小键盘 | 键盘非美国斜杠 | 0x0007 | 0x0064 | 0x0056 | 45 |
键盘/小键盘 | 键盘应用程序 | 0x0007 | 0x0065 | 0xE05D | 129 |
键盘/小键盘 | 键盘电源 | 0x0007 | 0x0066 | 0xE05E | |
键盘/小键盘 | 小键盘 = | 0x0007 | 0x0067 | 0x0059 | |
键盘/小键盘 | 键盘 F13 | 0x0007 | 0x0068 | 0x0064 | |
键盘/小键盘 | 键盘 F14 | 0x0007 | 0x0069 | 0x0065 | |
键盘/小键盘 | 键盘 F15 | 0x0007 | 0x006A | 0x0066 | |
键盘/小键盘 | 键盘 F16 | 0x0007 | 0x006B | 0x0067 | |
键盘/小键盘 | 键盘 F17 | 0x0007 | 0x006C | 0x0068 | |
键盘/小键盘 | 键盘 F18 | 0x0007 | 0x006D | 0x0069 | |
键盘/小键盘 | 键盘 F19 | 0x0007 | 0x006E | 0x006A | |
键盘/小键盘 | 键盘 F20 | 0x0007 | 0x006F | 0x006B | |
键盘/小键盘 | 键盘 F21 | 0x0007 | 0x0070 | 0x006C | |
键盘/小键盘 | 键盘 F22 | 0x0007 | 0x0071 | 0x006D | |
键盘/数字键盘 | 键盘 F23 | 0x0007 | 0x0072 | 0x006E | |
键盘/小键盘 | 键盘 F24 | 0x0007 | 0x0073 | 0x0076 | |
键盘/小键盘 | 小键盘 , | 0x0007 | 0x0085 | 0x007E | 107 *注意 4 |
键盘/小键盘 | 键盘国际 1 | 0x0007 | 0x0087 | 0x0073 | 56 *注释 4, 5 |
键盘/小键盘 | 键盘国际 2 | 0x0007 | 0x0088 | 0x0070 | 133 *注意 5 |
键盘/数字键盘 | 键盘国际3 | 0x0007 | 0x0089 | 0x007D | 14 *注意 5 |
键盘/小键盘 | 键盘国际4 | 0x0007 | 0x008A | 0x0079 | 132 *注意 5 |
键盘/小键盘 | 键盘国际 5 | 0x0007 | 0x008B | 0x007B | 131 *注意 5 |
键盘/小键盘 | 键盘国际6 | 0x0007 | 0x008C | 0x005C | |
键盘/小键盘 | 键盘 LANG1 | 0x0007 | 0x0090 | 0x0072 *注释 6 0x00F2 *注释 3、6 |
|
键盘/小键盘 | 键盘 LANG2 | 0x0007 | 0x0091 | 0x0071 *注释 6 0x00F1 *注释 3、6 |
|
键盘/小键盘 | 键盘 LANG3 | 0x0007 | 0x0092 | 0x0078 | |
键盘/数字键盘 | 键盘 LANG4 | 0x0007 | 0x0093 | 0x0077 | |
键盘/小键盘 | 键盘 LANG5 | 0x0007 | 0x0094 | 0x0076 | |
键盘/键区 | 键盘 LeftControl | 0x0007 | 0x00E0 | 0x001D | 58 |
键盘/小键盘 | 键盘 LeftShift | 0x0007 | 0x00E1 | 0x002A | 44 |
键盘/小键盘 | 键盘 LeftAlt | 0x0007 | 0x00E2 | 0x0038 | 60 |
键盘/数字键盘 | 键盘 Left GUI | 0x0007 | 0x00E3 | 0xE05B | 127 |
键盘/小键盘 | 键盘 RightControl | 0x0007 | 0x00E4 | 0xE01D | 64 |
键盘/小键盘 | 键盘 RightShift | 0x0007 | 0x00E5 | 0x0036 | 57 |
键盘/数字键盘 | 键盘 RightAlt | 0x0007 | 0x00E6 | 0xE038 | 62 |
键盘/小键盘 | 键盘 Right GUI | 0x0007 | 0x00E7 | 0xE05C | 128 |
消费者 | 扫描下一个曲目 | 0x000C | 0x00B5 | 0xE019 | |
消费者 | 扫描上一个曲目 | 0x000C | 0x00B6 | 0xE010 | |
消费者 | 停 | 0x000C | 0x00B7 | 0xE024 | |
消费者 | 播放/暂停 | 0x000C | 0x00CD | 0xE022 | |
消费者 | 静音 | 0x000C | 0x00E2 | 0xE020 | |
消费者 | 音量增大 | 0x000C | 0x00E9 | 0xE030 | |
消费者 | 音量减小 | 0x000C | 0x00EA | 0xE02E | |
消费者 | AL 使用者控制配置 | 0x000C | 0x0183 | 0xE06D | |
消费者 | AL 电子邮件阅读器 | 0x000C | 0x018A | 0xE06C | |
消费者 | AL 计算器 | 0x000C | 0x0192 | 0xE021 | |
消费者 | AL 本地计算机浏览器 | 0x000C | 0x0194 | 0xE06B | |
消费者 | AC 搜索 | 0x000C | 0x0221 | 0xE065 | |
消费者 | AC 主页 | 0x000C | 0x0223 | 0xE032 | |
消费者 | AC 返回 | 0x000C | 0x0224 | 0xE06A | |
消费者 | AC 向前 | 0x000C | 0x0225 | 0xE069 | |
消费者 | AC 停止 | 0x000C | 0x0226 | 0xE068 | |
消费者 | AC 刷新 | 0x000C | 0x0227 | 0xE067 | |
消费者 | AC 书签 | 0x000C | 0x022A | 0xE066 |
说明:
- Alt+Print screen 击键时发出 SysRq 键扫描代码
- Ctrl+Pause 击键时发出 Break 键扫描代码
- 如旧版键盘消息所示
- 键存在于巴西键盘上
- 键存在于日语键盘上
- 仅在密钥发布事件中发出扫描代码
扩展键标志
扩展键标志指示击键消息是否源自增强型 101/102 键键盘上的一个附加键。 扩展键包括键盘右侧的 Alt 和 Ctrl 键;数字键盘左侧键群中的 Insert*、Delete*、Home、End、Page up、Page down 和 Arrow 方向键;Num lock 键;Break (Ctrl+Pause) 键;Print screen 键;以及数字键盘中的 Divide (/) 和 Enter 键。 右侧 Shift 键不被视为扩展键,而是具有单独的扫描代码。
如果指定,扫描代码由两个字节序列组成,其中第一个字节的值为 0xE0。
上下文代码
上下文代码指示在生成击键消息时,Alt 键是否已按下。 如果 Alt 键被按下,代码为 1;如果键抬起,代码为 0。
上一个键状态标志
上一个键状态标志指示生成击键消息的键先前是否被按下。 如果键之前被按下,则为 1;如果键之前未按下,则为 0。 可以使用此标志识别键盘的自动重复功能生成的击键消息。 对于自动重复功能生成的 WM_KEYDOWN 和 WM_SYSKEYDOWN 击键消息,此标志设置为 1。 对于 WM_KEYUP 和 WM_SYSKEYUP 消息,它始终设置为 1。
转换状态标志
转换状态标志指示是按键还是释放键生成了击键消息。 对于 WM_KEYDOWN 和 WM_SYSKEYDOWN 消息,此标志始终设置为 0;对于 WM_KEYUP 和 WM_SYSKEYUP 消息,它始终设置为 1。
角色消息
击键消息提供有关击键的大量信息,但它们不提供字符击键的字符代码。 若要检索字符代码,应用程序必须在其线程消息循环中包含 TranslateMessage 函数。 TranslateMessage 将 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息传递到键盘布局。 布局检查消息的虚拟键代码,如果它对应于字符键,则提供等效的字符代码(考虑到 Shift 和 Caps Lock 键的状态)。 然后,它会生成包含字符代码的字符消息,并将消息置于消息队列的顶部。 消息循环的下一次迭代将从队列中删除字符消息,并将消息调度到相应的窗口过程。
本部分介绍以下主题:
非系统字符消息
窗口过程可以接收以下字符消息:WM_CHAR、WM_DEADCHAR、WM_SYSCHAR、WM_SYSDEADCHAR和 WM_UNICHAR。 TranslateMessage 函数在处理 WM_KEYDOWN 消息时生成 WM_CHAR 或 WM_DEADCHAR 消息。 同样,在处理 WM_SYSKEYDOWN 消息时,它会生成 WM_SYSCHAR 或 WM_SYSDEADCHAR 消息。
处理键盘输入的应用程序通常忽略除 WM_CHAR 和 WM_UNICHAR 消息之外的所有消息,并将任何其他消息传递给 DefWindowProc 函数。 请注意,WM_CHAR 使用 UTF-16(16 位 Unicode 转换格式)或 ANSI 字符集,而 WM_UNICHAR 始终使用 UTF-32(32 位 Unicode 转换格式)。 系统使用 WM_SYSCHAR 和 WM_SYSDEADCHAR 消息来实现菜单助记符。
所有字符消息的 wParam 参数包含按下的字符键的字符代码。 字符代码的值取决于接收消息的窗口的窗口类。 如果使用 RegisterClass 函数的 Unicode 版本来注册窗口类,则系统将 Unicode 字符提供给该类的所有窗口。 否则,系统提供 ANSI 字符代码。 有关详细信息,请参阅注册窗口类和在 Windows 应用中使用 UTF-8 代码页。
字符消息的 lParam 参数的内容与被转换为生成字符消息的按键消息的 lParam 参数的内容相同。 有关详细信息,请参阅 击键消息标志。
死字符消息
某些非英语键盘包含不应自行生成字符的字符键。 相反,它们用于向后续击键生成的字符添加变音符。 这些键称为 死键。 德语键盘上的绕行键是死键的一个示例。 若要输入包含带长音符的“o”的字符,德国用户会键入长音符键,然后键入“o”键。 具有键盘焦点的窗口将收到以下消息序列:
TranslateMessage 在处理来自死键的 WM_KEYDOWN 消息时,会生成 WM_DEADCHAR 消息。 尽管 WM_DEADCHAR 消息的 wParam 参数包含死键的变音符字符代码,但应用程序通常忽略该消息。 相反,它会处理后续击键生成的 WM_CHAR 消息。 WM_CHAR 消息的 wParam 参数包含带有变音符的字母的字符代码。 如果后续击键生成的字符不能与变音符组合,系统会生成两条 WM_CHAR 消息。 第一个参数的 wParam 包含变音符号的字符代码;第二个参数的 wParam 包含后续字符键的字符代码。
TranslateMessage 函数在处理来自系统死键(与 Alt 键一起按下的死键)的 WM_SYSKEYDOWN 消息时生成 WM_WM_SYSDEADCHAR 消息。 应用程序通常忽略 WM_SYSDEADCHAR 消息。
密钥状态
处理键盘消息时,应用程序可能需要确定另一个键的状态,除了生成当前消息的键。 例如,一个允许用户按 Shift+End 来选择文本块的字处理应用程序,在每次从 End 键收到击键消息时,都必须检查 Shift 键的状态。 应用程序可以使用 GetKeyState 函数来确定生成当前消息时虚拟密钥的状态;它可以使用 GetAsyncKeyState 函数来检索虚拟密钥的当前状态。
某些键被视为更改键盘布局状态的切换键。 切换键通常包括 Caps Lock(VK_CAPITAL)、Num Lock(VK_NUMLOCK),以及 滚动锁(VK_SCROLL)键。 大多数键盘都有这些键的相应 LED 指示器。
键盘布局保留名称列表。 生成单个字符的键的名称与键生成的字符相同。 非字符键(例如 Tab 和 Enter)的名称存储为字符串。 应用程序可以通过调用 GetKeyNameText 函数从键盘布局中检索任何键的名称。
击键和字符转换
该系统包括多个特殊用途函数,用于转换各种击键消息提供的扫描代码、字符代码和虚拟密钥代码。 这些函数包括 MapVirtualKey、ToAscii、ToUnicode和 VkKeyScan。
此外,Microsoft Rich Edit 3.0 支持 HexToUnicode IME,这允许用户使用热键在十六进制字符和 Unicode 字符之间进行转换。 这意味着,当 Microsoft Rich Edit 3.0 合并到应用程序中时,应用程序将继承 HexToUnicode IME 的功能。
热键支持
热键 是一种键组合,会生成 WM_HOTKEY 消息。系统将此消息放置在线程消息队列的顶部,绕过队列中的任何现有消息。 应用程序使用热键从用户获取高优先级键盘输入。 例如,通过定义由 Ctrl+C 按键组成的热键,应用程序可以允许用户取消长时间的操作。
若要定义热键,应用程序调用 RegisterHotKey 函数,指定生成 WM_HOTKEY 消息的键的组合、接收消息的窗口句柄以及热键的标识符。 当用户按下热键时,将 WM_HOTKEY 消息放置在创建窗口的线程的消息队列中。 消息的 wParam 参数包含热键的标识符。 应用程序可以为线程定义多个热键,但线程中的每个热键必须具有唯一标识符。 在应用程序终止之前,它应使用 UnregisterHotKey 函数来销毁热键。
应用程序可以使用热键控件,使用户能够轻松选择热键。 热键控件通常用于定义激活窗口的热键;它们不使用 RegisterHotKey 和 UnregisterHotKey 函数。 相反,使用热键控件的应用程序通常会发送 WM_SETHOTKEY 消息来设置热键。 每当用户按下热键时,系统就会发送一条 WM_SYSCOMMAND 消息,指定为 SC_HOTKEY。 有关热键控件的详细信息,请参阅 热键控件中的“使用热键控件”。
用于浏览和其他功能的键盘键
Windows 为键盘提供对浏览器功能、媒体函数、应用程序启动和电源管理的特殊键的支持。 WM_APPCOMMAND 支持额外的键盘键。 此外,修改 ShellProc 函数以支持额外的键盘键。
组件应用程序中的子窗口不太可能直接实现这些额外键盘键的命令。 因此,当按下其中一个键时,DefWindowProc 会将 WM_APPCOMMAND 消息发送到窗口。 DefWindowProc 还会将 WM_APPCOMMAND 消息发送到其父窗口(以气泡形式)。 这类似于使用鼠标右键调用上下文菜单的方式,即 DefWindowProc 在右键单击时发送 WM_CONTEXTMENU 消息,并将其发送到其父项(以气泡形式)。 此外,如果 DefWindowProc 收到顶级窗口的 WM_APPCOMMAND 消息,它将调用代码为 HSHELL_APPCOMMAND 的 shell 挂钩。
Windows 还支持 Microsoft IntelliMouse Explorer,这是一个具有五个按钮的鼠标。 这两个额外按钮支持向前和向后浏览器导航。 有关详细信息,请参阅XBUTTON。
模拟输入
若要模拟一系列不间断的用户输入事件,请使用 SendInput 函数。 该函数接受三个参数。 第一个参数 cInputs,指示将模拟的输入事件数。 第二个参数(rgInputs)是一个 INPUT 结构的数组,每个参数都描述一种类型的输入事件以及有关该事件的其他信息。 最后一个参数(cbSize)接受 INPUT 结构的大小(以字节为单位)。
SendInput 函数的工作原理是将一系列模拟输入事件注入到设备的输入流中。 效果类似于重复调用 keybd_event 或 mouse_event 函数,只是系统确保没有其他输入事件与模拟事件交织在一起。 调用完成后,返回值指示已成功播放的输入事件数。 如果此值为零,则阻止输入。
SendInput 函数不会重置键盘的当前状态。 因此,如果用户在调用此函数时按下了任何键,它们可能会干扰此函数生成的事件。 如果担心可能的干扰,请使用 GetAsyncKeyState 函数检查键盘的状态,并根据需要更正。
语言、区域设置和键盘布局
语言 是一种自然语言,如英语、法语和日语。 子语言 是特定地理区域中说的自然语言的变体,例如英国和美国说的英语子语言。 应用程序使用 语言标识符的值来唯一标识语言和子语言。
应用程序通常使用区域设置来设置处理输入和输出所使用的语言。 例如,设置键盘的区域设置会影响键盘生成的字符值。 设置显示器或打印机的区域设置会影响显示或打印的字形。 应用程序通过加载和使用键盘布局来设置键盘的区域设置。 他们通过选择支持指定区域设置的字体来设置显示或打印机的区域设置。
键盘布局不仅指定键盘上的键的物理位置,而且还确定通过按这些键生成的字符值。 每个布局标识当前输入语言,并确定由哪些键和键组合生成的字符值。
每个键盘布局都有一个相应的句柄,用于标识布局和语言。 句柄的低位字是语言标识符。 高位字是设备句柄,用于指定物理布局;或者为零,表示默认物理布局。 用户可以将任何输入语言与物理布局相关联。 例如,偶尔在法语中工作的英语用户可以将键盘的输入语言设置为法语,而无需更改键盘的物理布局。 这意味着用户可以使用熟悉的英语布局以法语输入文本。
应用程序通常不需要直接处理输入的语言。 相反,用户设置语言和布局组合,然后在它们之间切换。 当用户单击以其他语言标记的文本时,应用程序会调用 ActivateKeyboardLayout 函数来激活该语言的用户默认布局。 如果用户使用不在活动列表中的语言编辑文本,应用程序可以使用该语言调用 LoadKeyboardLayout 函数以获得基于该语言的布局。
ActivateKeyboardLayout 函数设置当前任务的输入语言。 hkl 参数可以是键盘布局的句柄,也可以是零扩展的语言标识符。 可以从 LoadKeyboardLayout 或 GetKeyboardLayoutList 函数获取键盘布局句柄。 HKL_NEXT 和 HKL_PREV 值也可用于选择下一个或上一个键盘。
GetKeyboardLayoutName 函数检索调用线程的活动键盘布局的名称。 如果应用程序使用 LoadKeyboardLayout 函数创建活动布局,GetKeyboardLayoutName 检索用于创建布局的同一字符串。 否则,该字符串是对应于活动布局的区域设置的主要语言标识符。 这意味着该函数不一定区分具有相同主要语言的不同布局,因此不能返回有关输入语言的特定信息。 但是,可以使用 GetKeyboardLayout 函数来确定输入语言。
LoadKeyboardLayout 函数加载键盘布局,并使布局可供用户使用。 应用程序可以使用 KLF_ACTIVATE 值立即激活当前线程的布局。 应用程序可以使用 KLF_REORDER 值对布局重新排序,而无需同时指定 KLF_ACTIVATE 值。 加载键盘布局时,应用程序应始终使用 KLF_SUBSTITUTE_OK 值,以确保选择用户的首选项(如果有)。
对于多语言支持,LoadKeyboardLayout 函数提供 KLF_REPLACELANG 和 KLF_NOTELLSHELL 标志。 KLF_REPLACELANG 标志指示函数在不更改语言的情况下替换现有键盘布局。 尝试使用相同的语言标识符替换现有布局但不指定 KLF_REPLACELANG 是错误的。 KLF_NOTELLSHELL 标志可阻止函数在添加或替换键盘布局时通知 shell。 这对于在连续一系列调用中添加多个布局的应用程序非常有用。 除最后一次调用外,所有调用都应使用此标志。
UnloadKeyboardLayout 函数受到限制,因为它无法卸载系统默认输入语言。 这可确保用户始终有一种布局可用于输入文本,所用字符集与 shell 和文件系统使用的字符集相同。