键盘用于几种不同类型的输入,包括:
- 字符输入。用户输入到文档或编辑框中的文本。
- 键盘快捷键。调用应用程序函数的击键;例如,用CTRL+O打开一个文件。
- 系统命令。调用系统函数的击键;例如,ALT+TAB切换窗口。
在考虑键盘输入时,重要的是要记住键击与字符不一样。例如,按A键可能会导致以下任何字符。
- a
- A
- á(如果键盘支持组合变音符号)
此外,如果按住ALT键,则按下A键会产生ALT+A,系统根本不会将其视为字符,而是作为系统命令。
键码
当您按下某个键时,硬件会生成一个扫描码。每个键盘的扫描码都不相同,扫描码也有不同的扫描码。您几乎不会关心扫描码。键盘驱动程序将扫描码转换为虚拟键码。虚拟键码是设备无关的。按任意键盘上的A键将生成相同的虚拟键码。
一般来说,虚拟键码不对应于ASCII码或任何其他字符编码标准。这一点很明显,如果你考虑一下,因为同一个键可以生成不同的字符(a,A,á),而某些键(如功能键)不对应任何字符。
也就是说,下面的虚拟键代码映射到ASCII等价物:
- ‘0’到’9’键=ASCII ‘0’ - ‘9’(0x30 - 0x39)
- ‘A’到’Z’键= ASCII ‘A’ - ‘Z’(0x41 - 0x5A)
在某些方面,这种映射是不幸的,因为你不应该把虚拟键代码看作字符。
头文件WinUser.h为大多数虚拟键代码定义了常量。例如,左箭头键的虚拟键码是VK_LEFT(0x25)。有关虚拟密钥代码的完整列表,请参阅虚拟密钥代码。没有为符合ASCII值的虚拟键代码定义常量。例如,A键的虚拟键码是0x41,但是没有名为VK_A的常量。相反,只需使用数字值。
按键和松开键消息
当您按下一个键时,具有键盘焦点的窗口会收到以下消息之一。
WM_SYSKEYDOWN
消息表示一个系统键,它是调用系统命令的关键笔划。有两种类型的系统密钥:
- ALT+任意键
- F10
F10键激活一个窗口的菜单栏。各种ALT键组合调用系统命令。例如,ALT+TAB切换到一个新的窗口。另外,如果一个窗口有一个菜单,ALT键可以用来激活菜单项。一些ALT键组合不做任何事情。
所有其他键击被认为是非系统键并产生WM_KEYDOWN消息。这包括F10以外的功能键。
当您松开键时,系统会发送相应的密钥消息:
如果按住一个键足够长的时间以启动键盘的重复功能,则系统将发送多个按键消息,然后发送一个松开键消息。
在到目前为止讨论的所有四种键盘消息中,wParam参数都包含密钥的虚拟密钥代码。lParam参数包含一些打包成32位的杂项信息。您通常不需要lParam中的信息。一个可能有用的标志是位30,“前一个键状态”标志,对于重复的键控消息,该标志被设置为1。
顾名思义,系统按键主要用于操作系统。如果拦截WM_SYSKEYDOWN消息,则调用DefWindowProc。否则,您将阻止操作系统处理该命令。
字符消息
通过TranslateMessage函数将关键笔划转换为字符,这是我们在模块1中首次看到的。该函数检查按键消息并将其转换为字符。对于生成的每个字符,TranslateMessage函数在窗口的消息队列中放置WM_CHAR或WM_SYSCHAR消息。消息的wParam参数包含UTF-16字符。
正如你可能猜到的,WM_CHAR消息是从WM_KEYDOWN消息生成的,而WM_SYSCHAR消息是从WM_SYSKEYDOWN消息生成的。例如,假设用户按下SHIFT键,然后按A键。假设一个标准的键盘布局,你会得到以下消息序列:
WM_KEYDOWN:SHIFT
WM_KEYDOWN:A
WM_CHAR:’A’
另一方面,组合ALT+P将生成:
WM_SYSKEYDOWN:VK_MENU
WM_SYSKEYDOWN:0x50
WM_SYSCHAR:’p’
WM_SYSKEYUP:0x50
WM_KEYUP:VK_MENU
(由于历史原因,ALT键的虚拟键代码被命名为VK_MENU。)
WM_SYSCHAR消息指示一个系统字符。和WM_SYSKEYDOWN一样,你通常应该直接把这个消息传递给DefWindowProc。否则,您可能会干扰标准的系统命令。特别是,不要将WM_SYSCHAR视为用户输入的文本。
WM_CHAR消息是你通常认为的字符输入。该字符的数据类型是wchar_t,表示一个UTF-16 Unicode字符。字符输入可以包含ASCII范围之外的字符,特别是在美国以外常用的键盘布局。您可以通过安装区域键盘然后使用屏幕键盘功能来尝试不同的键盘布局。
用户还可以安装输入法编辑器(IME),以使用标准键盘输入复杂脚本(如日文字符)。例如,使用日语IME输入片假名字符カ(ka),可能会收到以下消息:
WM_KEYDOWN:VK_PROCESSKEY(IME PROCESS键)
WM_KEYUP:0x4B
WM_KEYDOWN:VK_PROCESSKEY
WM_KEYUP:0x41
WM_KEYDOWN:VK_PROCESSKEY
WM_CHAR:カ
WM_KEYUP:VK_RETURN
一些CTRL组合键被转换成ASCII控制字符。例如,CTRL + A被转换为ASCII ctrl-A(SOH)字符(ASCII值0x01)。对于文本输入,通常应该过滤出控制字符。另外,避免使用WM_CHAR来实现键盘快捷键。而是使用WM_KEYDOWN消息;甚至更好,使用加速器表。加速器表在下一个主题“加速器表”中进行介绍。
以下代码显示调试器中的主键盘消息。尝试使用不同的击键组合,并查看生成的消息。
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
wchar_t msg[32];
switch(uMsg){
case WM_SYSKEYDOWN:
swprintf_s(msg,L"WM_SYSKEYDOWN: 0x%x\n",wParam);
OutputDebugString(msg);
break;
case WM_SYSCHAR:
swprintf_s(msg,L"WM_SYSCHAR: %c\n",(wchar_t)wParam);
OutputDebugString(msg);
break;
case WM_SYSKEYUP:
swprintf_s(msg,L"WM_SYSKEYUP: 0x%x\n",wParam);
OutputDebugString(msg);
break;
case WM_KEYDOWN:
swprintf_s(msg,L"WM_KEYDOWN: 0x%x\n",wParam);
OutputDebugString(msg);
break;
case WM_KEYUP:
swprintf_s(msg,L"WM_KEYUP: 0x%x\n",wParam);
OutputDebugString(msg);
break;
case WM_CHAR:
swprintf_s(msg,L"WM_CHAR: %c\n",(wchar_t)wParam);
OutputDebugString(msg);
break;
/* Handle other messages (not shown) */
}
return DefWindowProc(m_hwnd,uMsg,wParam,lParam);
}
杂项键盘消息
其他键盘消息可以安全地被大多数应用程序忽略。
- WM_DEADCHAR消息是发送一个组合键,如一个变音符号。例如,在西班牙语键盘上,输入accent(‘)后跟E将产生字符é。 WM_DEADCHAR被发送为重音字符。
- WM_UNICHAR消息已过时。它使ANSI程序能够接收Unicode字符输入。
- 当IME将按键序列转换为字符时,将发送WM_IME_CHAR字符。除了通常的WM_CHAR消息之外,它还被发送。
键盘状态
键盘消息是事件驱动的。也就是说,当一些有趣的事情发生的时候,你会得到一个消息,比如一个按键,这个消息告诉你刚发生的事情。但是你也可以随时通过调用GetKeyState函数来测试一个键的状态。
例如,考虑如何检测鼠标左键+ALT键的组合。您可以通过侦听关键笔画消息并存储标志来跟踪ALT键的状态,但GetKeyState为您节省了麻烦。当您收到WM_LBUTTONDOWN消息时,只需调用GetKeyState,如下所示:
if(GetKeyState(VK_MENU)&0x8000)){
// ALT key is down.
}
GetKeyState消息将虚拟键码作为输入,并返回一组位标志(实际上只是两个标志)。值0x8000包含测试键是否被按下的位标志。
大多数键盘都有左右两个ALT键。前面的例子测试是否被按下。您还可以使用GetKeyState来区分ALT,SHIFT或CTRL键的左侧和右侧实例。例如,下面的代码测试是否按下右ALT键。
if(GetKeyState(VK_RMENU)&0x8000)){
// Right ALT key is down.
}
GetKeyState函数很有趣,因为它报告了一个虚拟键盘状态。此虚拟状态基于消息队列的内容,并在您从队列中删除消息时得到更新。当程序处理窗口消息时,GetKeyState会在每条消息排队时为您提供键盘快照。例如,如果队列中的最后一条消息是WM_LBUTTONDOWN,GetKeyState将在用户单击鼠标按钮时报告键盘状态。
由于GetKeyState基于消息队列,因此它也会忽略发送到另一个程序的键盘输入。如果用户切换到另一个程序,发送到该程序的任何按键被GetKeyState忽略。如果你真的想知道键盘的直接物理状态,有一个函数:GetAsyncKeyState。但是,对于大多数UI代码,正确的函数是GetKeyState。
下一个
加速器表
原文链接:Keyboard Input