前面讨论了利用位移状态信息把按键消息翻译为字符消息的方法,并且提到,仅利用转换状态信息还不够,因为还需要知道与国家/地区有关的键盘配置。由于这个原因,您不应该试图把按键消息翻译为字符代码。Windows会为您完成这一工作,在前面我们曾看到过以下的程序代码:
while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; }
这是WinMain中典型的消息循环。GetMessage函数用队列中的下一个消息填入msg结构的字段。DispatchMessage以此消息为参数呼叫适当的窗口消息处理程序。
在这两个函数之间是TranslateMessage函数,它将按键消息转换为字符消息。如果消息为WM_KEYDOWN或者WM_SYSKEYDOWN,并且按键与位移状态相组合产生一个字符,则TranslateMessage把字符消息放入消息队列中。此字符消息将是GetMessage从消息队列中得到的按键消息之后的下一个消息。
消息顺序
因为TranslateMessage函数从WM_KEYDOWN和WM_SYSKEYDOWN消息产生了字符消息,所以字符消息是夹在按键消息之间传递给窗口消息处理程序的。例如,如果Caps Lock未打开,而使用者按下再释放A键,则窗口消息处理程序将接收到如表6-10所示的三个消息:
表6-10 |
消息 |
按键或者代码 |
WM_KEYDOWN |
「A」的虚拟键码(0x41) |
WM_CHAR |
「a」的字符代码(0x61) |
WM_KEYUP |
「A」的虚拟键码(0x41) |
如果您按下Shift键,再按下A键,然后释放A键,再释放Shift键,就会输入大写的A,而窗口消息处理程序会接收到五个消息,如表6-11所示:
表6-11 |
消息 |
按键或者代码 |
WM_KEYDOWN |
虚拟键码VK_SHIFT (0x10) |
WM_KEYDOWN |
「A」的虚拟键码(0x41) |
WM_CHAR |
「A」的字符代码(0x41) |
WM_KEYUP |
「A」的虚拟键码(0x41) |
WM_KEYUP |
虚拟键码VK_SHIFT(0x10) |
Shift键本身不产生字符消息。
如果使用者按住A键,以使自动重复产生一系列的按键,那么对每条WM_KEYDOWN消息,都会得到一条字符消息,如表6-12所示:
表6-12 |
消息 |
按键或者代码 |
WM_KEYDOWN |
「A」的虚拟键码(0x41) |
WM_CHAR |
「a」的字符代码(0x61) |
WM_KEYDOWN |
「A」的虚拟键码(0x41) |
WM_CHAR |
「a」的字符代码(0x61) |
WM_KEYDOWN |
「A」的虚拟键码(0x41) |
WM_CHAR |
「a」的字符代码(0x61) |
WM_KEYDOWN |
「A」的虚拟键码(0x41) |
WM_CHAR |
「a」的字符代码(0x61) |
WM_KEYUP |
「A」的虚拟键码(0x41) |
如果某些WM_KEYDOWN消息的重复计数大于1,那么相应的WM_CHAR消息将具有同样的重复计数。
组合使用Ctrl键与字母键会产生从0x01(Ctrl-A)到0x1A(Ctrl-Z)的ASCII控制代码,其中的某些控制代码也可以由表6-13列出的键产生:
表6-13 |
按键 |
字符代码 |
产生方法 |
ANSI C控制字符 |
Backspace |
0x08 |
Ctrl-H |
/b |
Tab |
0x09 |
Ctrl-I |
/t |
Ctrl-Enter |
0x0A |
Ctrl-J |
/n |
Enter |
0x0D |
Ctrl-M |
/r |
Esc |
0x1B |
Ctrl-[ |
最右列给出了在ANSI C中定义的控制字符,它们用于描述这些键的字符代码。
有时Windows程序将Ctrl与字母键的组合用作菜单快捷键此时,不会将字母键转换成字符消息。
经过10年的考虑(回顾这些年来我写过的Windows程序代码),我更喜欢将Tab、Enter、Backspace和Escape键处理成控制字符,而不是虚拟键。我通常这样处理WM_CHAR:
case WM_CHAR: //其它行程序 switch (wParam) { case '/b': // backspace //其它行程序 break ; case '/t': // tab //其它行程序 break ; case '/n': // linefeed //其它行程序 break ; case '/r': // carriage return //其它行程序 break ; default: // character codes //其它行程序 break ; } return 0 ;