参考:https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
应用程序获取键盘输入的内容是以post到应用程序框口的消息的形式。
Keyboard input model
通过给现存的键盘安装键盘设备驱动,操作系统为应用提供了与具体键盘设备无关的键盘支持。通过使用应用程序或者用户选定的特定语言的键盘布局,操作系统提供了与具体语言无关的键盘支持。键盘设备驱动从键盘中获取扫描码,扫描码在键盘布局中被转换为消息并Post到应用程序的窗口中。
键盘上每个按键都对应一个独一无二的值,这个值就是扫描码。扫描码是与设备相关的。当用户按键时,键盘会产生两个键盘码,一个是当按键按下时产生,另一个是按键松开时产生。
键盘设备驱动会处理扫描码并将扫描码映射到一个虚拟键码。虚拟键码是系统定义的与设备无关的值,它的作用是标识一个按键的作用。在完成映射之后,键盘布局将创建一个包含此扫描码、虚拟键码和其它与此次按键相关信息的message,然后,将此消息放入系统消息队列。
keyboard focus and activation
系统将键盘消息post到foreground 线程, foreground线程是创建了带 keyboard focus属性的window的线程。一个拥有keyboard focus属性的窗口会接受所有键盘消息,直到此窗口不再具有此属性为止。线程可以调用函数GetFocus来获取现在哪个窗口拥有keyboard focus 属性,调用SetFocus则是设置哪个窗口能够拥有keyboard focus属性。当keyboard focus属性在窗口之间传递时,系统会向失去此属性的窗口发送WM_KILLFOCUS消息,向获取此属性的窗口发送WM_SETFOCUS消息。keyboard focus是与active window是有一定关系的。active window是用户正在操作的window,是top-level window,而拥有keyboard focus属性的窗口可以是active window,也可以是active window的子窗口。为了便于识别active window,系统将active window 放在 z order或者说是z坐标正向的顶部,并且使此窗口的边界和标题栏高亮显示。当一个窗口变为不激活状态,而另一个窗口变为激活状态时, 系统会发送WM_ACTIVE消息,消息中wParam成员的低字为0,用以表明此窗口是非激活状态,其值为非0时,表示此窗口是激活状态。当默认窗口处理函数收到此消息时,会将active window的窗口属性设置为keyboard focus.
Keystroke Messages
按下键盘键时WM_KEYDOWN或者WM_SYSKEYDOWN 消息会放入与窗口绑定的线程的消息队列,且此窗口需要由keyboard focus属性。当释放按键时在同样的队列会放入WM_KEYUP或者WM_SYSKEYUP消息。key-up和key-down消息通常都是成对出现的,如果用户长时间按住一个键,系统会生成连续的WM_KEYDOWN或者WM_SYSKEYDOWN消息,当松开按键时,只会发送一个WM_KEYUP或者WM_SYSKEYUP消息。
sytem and nonsystem keystrokes
系统会对系统按键和非系统按键进行区分。系统按键会产生WM_SYSKEYDOWN和WM_SYSKEYUP消息,非系统按键会 产生WM_KEYDOWN和WM_KEYUP消息。
系统按键消息通常由系统使用。它们的主要作用是为窗口窗口菜单提供内置键盘接口,使用这些接口可以控制哪个窗口是active。系统按键消息的产生主要是用户使用ALT组合键,或者当用户敲击键盘,但是没有窗口具有keyboard focus属性时产生。此时,键盘消息都被放入active window 的消息队列。
应用程序通常使用非系统按键消息。DefWindowProc函数不会对这些消息进行处理。window procedure能够丢弃任何它不需要的非系统按键消息。
Virtual-key codes described
键盘敲击消息结构中的wParam成员包含被按下或者释放的按键的virtual-key code。 window procedure会不会忽略键盘敲击消息是取决于virtual-key code 的值的。通常,window procedure仅仅处理一小部分它所收到的键盘敲击消息而忽略大部分此类消息。window precedure不会处理来自于字符的键盘敲击消息。来自于字符的键盘敲击信息会通过函数TranslateMessage将此类消息转换为字符消息。
Keystroke Message Flags
键盘敲击消息结构中的成员lParam 包含其它关于生成此消息的信息。这些信息包括 repeat count、scan code、 extended-key flag、context code、previous key-state flag、transition-state flag。如图:
应用程序可以使用下图所示宏来获取lparam中高字中关于键盘敲击的flag.
Scan code
扫描码是当用户按压按键时,由系统产生的一个数值。这个数值用于标识被按下的按键,此数值与此时正在使用的键盘布局无关。键盘上按键表示的字符是与键盘布局相关。应用程序一般会忽略扫描码,它会使用虚拟键码来识别按键点击消息。现代键盘使用HID 协议与电脑交互。 键盘驱动将键盘发送的HID数据转换成扫描码并发送到应用程序。扫描码如下图:
Extended-Key Flag
此标记用于说明键盘按键消息是否来源于Enhanced 101/102键盘中的扩展键。
Context Code
此标记用于说明键盘按键消息生成时,ALT键是否按下。值为1时表示按下,0表示弹起。
Previous Key-State Flag
此标记用于说明导致键盘按键消息生成的按键之前是按下的状态还是弹起的状态。当值为1时表示之前是按下状态,值为0时表示之前是弹起状态。此标记可以用来识别键盘按键消息是否是由键盘的automatic repeat feature产生。当WM_KEYDOW或者WM_SYSKEYDOWN是由键盘的automatic repeat feature产生,则此标记设置为1。.WM_KEYUP和WM_SYSKEYUP消息的此标记总是为1。
Transiton-State Flag
此标记用于说明键盘按键消息是因为按下按键产生的还是释放按键产生的。对于WM_KEYDOWN和WM_SYSKEYDOWN消息来说此值总是0,对于WM_KEYUP和WM_SYSYKEYUP来说总是1.