Windows程序获得键盘输入的方式:键盘输入以消息的形式传递给程序的窗口过程。Windows用8种不同消息传递不同键盘事件。似乎有点多,但程序可以忽略至少一半的消息而不会有任何问题。
焦点
键盘必须被Windows下运行所有应用程序共享,DispatchMessage函数向窗口过程发送消息,有输入焦点的窗口是活动窗口或活动窗口的派生窗口。
击键和字符
应用程序从Windows接受的键盘事件可以分为击键和字符两类。对可显示字符的击键组合,Windows不仅给程序发送击键消息,而且还发送字符消息。有些键不产生字符,对这些键Windows只产生击键消息。
系统击键与非系统击键
系统击键表示该击键对Windows比对Windows程序更加重要。程序通常忽略系统击键消息,并将他们传送到DefWindowProc。如果想在自己的窗口过程中包括不会系统击键的代码,那么在处理这些消息之后再传送到DefWindowProc,Windows就仍然可以将他们由于通常的目的。如果不将它传送给DefWindowProc那么Windows就不会对消息进行处理。
虚拟键码
虚拟键码保存在击键消息(WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP)的wParam中。
lParam信息
4个击键消息的lParam含有对了解击键非常有用的其他信息。lParam32位分为6个域:
0~15:重复计数,表示发送击键后程序没能处理的个数。
16~23:OEM扫描码,由硬件键盘产生的代码。
24:扩展标志,当击键来自IBM增强键盘的附加键之一。那么扩展标志为1。指的是键盘右端的Alt和Ctrl,以及不是数字键盘的那部分光标移动键的副本。
29:环境代码,在按下Alt后为1。
30:键的先前状态,如果键之前是释放的,则先前为0,反之为1。
31:转换状态,如果键被按下为0,如果被释放为1。
换档状态
当处理消息时可能需要知道是否按下了换档键(Shift、Ctrl和Alt)或开关键(CapsLock、NumLock和ScrollLock)。通过调用GetKeyState函数,就能获得此信息。
GetKeyState,可以使用VK_LSHIFT,VK_RSHIFT等标识符来确定按下的键是左边的还是右边的。不过大多数需要检测鼠标键和奸计组合的Windows程序都用其他的做法来做到这一点――即在收到鼠标键时检查击键。
注意:GetKeyState并非实时检查键盘状态,而只是检查当前正在处理的消息之前和发生时的键盘状态。
使用击键消息
Windows程序通常为不产生字符的击键使用WM_KEYDOWN消息。虽然可以借助击键消息和换档状态信息能将击键消息转换为字符消息。但是不要这样做。因为这样会遇到国际间键盘不同所带来的问题。
因此,多数情况下处理光标移动键的WM_KEYDOWN消息。在使用这些击键时,可以通过GetKeyState来检查Shift和Ctrl。
字符消息
前面提到不要将击键转换为字符。所以Windows会通过TranslateMessage完成这个工作。
四类字符消息
WM_CHAR和WM_DEADCHAR从WM_KEYDOWN得到。WM_SYSCHAR和WM_SYSDEADCHAR从WM_SYSKEYDOWN得到。
这四个字符消息的lParam参数同产生字符消息的击键消息。wParam则是ANSI或Unicode字符代码。通过IsWindowUnicode可以确定得到的是何种字符编码。
每当有一个击键消息产生TranslateMessage会判断它是否为字符消息,如果是它将产生一个字符消息。但Ctrl与字母组合不会产生字符消息。因为加速键函数会在它之前将消息转换。
插入符
当想程序中输入文本时,通常有一个下划线,竖条或者方块来指示输入的下一个字符将出现在屏幕上的位置。Windows中称它为“插入符”。
主要有5个插入符函数:
CreateCaret创建插入符。
SetCaretPos在窗口中设置插入符位置。
ShowCaret显示插入符。
HideCaret隐藏插入符。
DestroyCaret撤销插入符。
另外还有获取插入符当前位置GetCaretPos和获取与设置插入闪烁时间GetCaretBlinkTime和SetCaretBlinkTime的函数。
只有当窗口有输入焦点时,窗口内显示的插入符才有意义。所以通过处理WM_SETFOCUS和WM_KILLFOCUS消息,程序就能确定它是否有输入焦点。这些消息成对出现。所以在WM_SETFOCUS期间调用CreateCaret在WM_KILLFOCUS期间调用DestroyWindow。
插入符刚创建时是隐藏的。如果要插入符可见,那么在CreateCaret后还要调用ShowCaret。另如果窗口过程在绘制某些东西时,它必须调用HideCaret隐藏插入符。多次调用HideCaret后只有在调用同样数量的ShowCaret才能看到插入符。