键盘消息
键盘以消息的形式传递给程序的窗口过程。
默认情况下,一些系统功能键 (如:Alt, Ctrl) 的消息由 DefWindowProc() 处理。
接受到键盘事件的窗口称为有输入焦点的窗口。具有输入焦点的窗口要么是活动窗口,要么是活动窗口的子孙窗口。
活动窗口总是最上层的窗口。
窗口通过捕获 WM_SETFOCUS 和 WM_KILLFOCUS 消息来确定自己的窗口是否具有输入焦点。
当用户按下和释放键盘上的一个键时,Windows 和 键盘设备驱动程序 将硬件扫描码转换为格式化后的消息。
但是这些消息并不立即放入应用程序消息队列,而是由 Windows 先把这些消息存储在系统消息队列中。
系统消息队列是一个单独的消息队列,它被 Windows 用来初步存储用户从键盘和鼠标输入的消息。
仅当 Windows 应用程序完成了对前一个用户输入消息的处理后,Windows 才从系统消息队列中取出下一条消息,
并把它放入应用程序消息队列。
(即先把消息存储在系统消息队列里,再把它们发送到应用程序消息队列)
原因是需要同步,被期望接受键盘输入的窗口是具有活动焦点的窗口。
用户输入的速度可能快于应用程序能处理的击键动作,而一个特殊的击键可能会使焦点从一个窗口转换到另一个窗口,
后续的击键也应该跟着到了另一个窗口。
当用户按下一个键时,Windows 将 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息放入具有输入焦点的窗口的消息队列中。
当该键被释放时,Windows 把 WM_KEYUP 或 WM_SYSKEYUP 消息放入相应的消息队列中。
键按下 | 键释放 | |
非系统击键 | WM_KEYDOWN | WM_KEYUP |
系统击键 | WM_SYSKEYDOWN | WM_SYSKEYUP |
通常键按下消息和释放消息时成对出现的。但是如果你按下一个键不放时,则被认为发生了一次连续按键(自动重复)行为。
Windows 将发送给窗口过程一连串的 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息。
当此键最终被释放时,Windows 发送给窗口过程一个 WM_KEYUP 或 WM_SYSKEYUP 消息。
像所有消息队列一样,击键消息是可被实时追踪的。你可调用 GetMessageTime(),得到键被按下或释放的相对时间。
WM_SYSKEYDOWN 和 WM_SYSKEYUP 中的 “SYS" 代表系统,它表明该击键对 Windows 比对 Windows 应用程序更加重要。
当输入键 和 Alt 键组合时通常产生的是 WM_SYSKEYDOWN 和 WM_SYSKEYUP 消息。
对 WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP 这四类击键消息,
这些消息存储在 wParam 中,wParam 是虚拟键代码,用于标识哪个键被按下或被释放。
而 lParam 包含属于本次击键的一些其他数据。
你经常使用的大多数虚拟键代码命名是这样的: VK_***。
下表列出了这些虚拟键代码的名称和数值:
十进制 | 十六进制 | WINUSER.H 中的标识符 | 鼠标左键 |
1 | 01 | VK_LBUTTON | 鼠标左键 |
2 | 02 | VK_RBUTTON | 鼠标右键 |
3 | 03 | VK_CANCEL | Ctrl-Break |
4 | 04 | VK_MBUTTON | 鼠标中键 |
8 | 08 | VK_BACK | 退格键(Backspace) |
9 | 09 | VK_TAB | Tab 键 |
13 | 0D | VK_RETURN | 回车键 |
16 | 10 | VK_SHIFT | Shift 键 |
17 | 11 | VK_CONTROL | Ctrl 键 |
18 | 12 | VK_MENU | Alt 键 |
19 | 13 | VK_PAUSE | Pause 键 |
20 | 14 | VK_CAPITAL | 大写锁定键 |
27 | 1B | VK_ESCAPE | Esc 键 |
32 | 20 | VK_SPACE | 空格键 |
33 | 21 | VK_PRIOR | Page Up 键 |
34 | 22 | VK_NEXT | Page Down 键 |
35 | 23 | VK_END | End 键 |
36 | 24 | VK_HOME | Home 键 |
37 | 25 | VK_LEFT | 左箭头 |
38 | 26 | VK_UP | 上箭头 |
39 | 27 | VK_RIGHT | 右箭头 |
40 | 28 | VK_DOWN | 下箭头 |
44 | 2C | VK_SNAPSHOT | Print Screen 键 |
45 | 2D | VK_INSERT | Insert 键 |
46 | 2E | VK_DELETE | Del 键 |
48~57 | 30~39 | 无 | 主键盘上的 0~9 |
65~90 | 41~5A | 无 | A~Z |
91 | 5B | VK_LWIN | 左 Windows 键 |
92 | 5C | VK_RWIN | 右 Windows 键 |
96~105 | 60~69 | VK_NUMPAD0~VK_NUMPAD9 | 数字锁定键打开时数字小键盘的 0~9 |
106 | 6A | VK_MULTIPLY | 数字键区的 * |
107 | 6B | VK_ADD | 数字键区的 + |
109 | 6D | VK_SUBTRACT | 数字键区的 - |
110 | 6E | VK_DECIMAL | 数字键区的 . |
111 |
6F | VK_DIVIDE | 数字键区的 / |
112~123 |
70~7B | VK_F1~VK_F12 | 功能键 F1 ~ F24 |
144 |
90 | VK_NUMLOCK | 数字锁定键 |
145 |
91 | VK_SCROLL | Scroll Lock 键 |
(注意,数字键和字母键的虚拟键代码就是 ASCII 码)
lParam 信息:
wParam 消息参数包含虚拟键代码,lParam 消息参数包含了帮助理解击键的其他有用信息。
32 位的 lParam 消息被分成了 6 个字段,如图:
重复计数:
重复计数是消息所表示的击键数目。大多数情况下,它被设置为 1。
但是,如果你按下一个键不放,且窗口过程不足够快,跟不上输入速率来处理击键消息,
Windows 就会把一些 WM_KEYDOWN 和 WM_SYSKEYDOWN 消息合并成一个单独的消息,并相应增加重复计数字段。
WM_KEYUP 和 WM_SYSKEYUP 消息的重复计数总是 1。
OEM 扫描码:
OEM 扫描码是键盘硬件产生的代码,由于严重依赖于键盘上键的分布,我们现在不再需要这种东西了。
扩展键标记:
Windows 程序通常忽略扩展键标记。
内容代码:
如果在击键的同时也按下了 Alt 键,则内容代码为 1。WM_SYSKEYUP 和 WM_SYSKEYDOWN 消息的此位始终为 1,
而 WM_KEYUP 和 WM_KEYDOWN 消息的此位始终为 0。有两种情况例外:
如果活动窗口最小化了,则它不具有输入焦点。所有的击键将产生 WM_SYSKEYUP 和 WM_SYSKEYDOWN 消息。
如果 Alt 键未被按下,内容代码字段将被设置为 0。
Windows 处理 WM_SYSKEYUP 和 WM_SYSKEYDOWN 消息,使最小化的活动窗口不处理这些击键。
在某些非英语键盘上,一些字符是通过 Shift 键,Ctrl 键 或 Alt 键同另一个键的组合产生的。
在这些情况下,内容代码被设置为 1,但消息并不是系统击键消息。
键的先前状态:
如果键以前是处于释放状态的,则键的先前状态为 0。而如果键以前是按下的,则键的先前状态为 1。
WM_KEYUP 和 WM_SYSKEYUP 消息的此字段总是为 1。
但 WM_KEYDOWN 和 WM_SYSKEYDOWN 消息的此字段可能为 0 或 1。
该位为 1 表明,消息为重复击键产生的第二个或后续发出的消息。
转换状态:
如果键正在被按下,转换状态为 0;如果键正在被释放,转换状态为 1。
WM_KEYDOWN 和 WM_SYSKEYDOWN 消息的此字段设置为 0,而 WM_KEYUP 和 WM_SYSKEYUP 消息的此字段设置为 1。
转义状态:
当处理击键消息时,你可能需要知道是否有转义键(Shift 键,Ctrl 键 和 Alt 键)
或切换键(Caps Lock 键,Num Lock 键 和 Scroll Lock 键) 被按下。
你能通过调用 GetKeyState() 获得此消息。
例如:iState = GetKeyState(VK_SHIFT) ;
如果 Shift 键被按下,则 iState 变量为负。如果 Caps Lockk 键打开,则从返回的值最低位置为 1。此位与键盘上的小灯保持一致。
GetMessageTime() 介绍:
功能:该函数返回由GetMessage从当前线程队列里取得上一消息的消息时间。
时间是一个长整数,指定从系统开始到消息创建(即,放入线程消息队列)的占用时间(按毫秒计算)。
函数原型:LONG GetMessageTime(VOID) ;
GetKeyState() 介绍:
功能:该函数检取指定虚拟键的状态。
该状态指定此键是UP状态,DOWN状态,还是被触发的(开关每次按下此键时进行切换)。
函数原型:SHORT GetKeyState(int nVirtKey) ;
SendMessage() 介绍:
功能:该函数将指定的消息发送到一个或多个窗口。
此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。
函数原型:LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM IParam) ;
参数: