读书人

Windows程式开发设计指南(6) 键盘

发布时间: 2012-09-18 16:21:42 作者: rapoo

Windows程式开发设计指南(六) 键盘

6. 键盘

在Microsoft Windows 98中,键盘和滑鼠是两个标准的使用者输入来源,在一些连贯操作中常产生互补作用。当然,滑鼠在今天的应用程式中比十年前使用得更为广泛。甚至在一些应用程式中,我们更习惯於使用滑鼠,例如在游戏、画图程式、音乐程式以及Web浏览器等程式中就是这样。然而,我们可以不使用滑鼠,但绝对不能从一般的PC中把键盘拆掉。

相对於个人电脑的其他元件,键盘有非常久远的历史,它起源於1874年的第一台Remington打字机。早期的电脑程式员用键盘在Hollerith卡片上打孔,後来在终端机上用键盘直接与大型主机沟通。PC上的键盘在某些方面进行了扩充,加上了功能键、游标移动键和单独的数字键盘,但它们的输入原理基本相同。

键盘基础
 

您大概已经猜到Windows程式是如何获得键盘输入的:键盘输入以讯息的形式传递给程式的视窗讯息处理程式。实际上,第一次学习讯息时,键盘事件就是一个讯息如何将不同型态资讯传递给应用程式的显例。

Windows用八种不同的讯息来传递不同的键盘事件。这好像太多了,但是(就像我们所看到的一样)程式可以忽略其中至少一半的讯息而不会有任何问题。并且,在大多数情况下,这些讯息中包含的键盘资讯会多於程式所需要的。处理键盘的部分工作就是识别出哪些讯息是重要的,哪些是不重要的。

忽略键盘
 

虽然键盘是Windows程式中使用者输入的主要来源,但是程式不必对它接收的所有讯息都作出回应。Windows本身也能处理许多键盘功能。

例如,您可以忽略那些属於系统功能的按键,它们通常用到Alt键。程式不必监视这些按键,因为Windows会将按键的作用通知程式(当然,如果程式想这么做,它也能监视这些按键)。虽然呼叫程式功能表的按键将通过视窗的视窗讯息处理程式,但通常内定的处理方式是将按键传递给DefWindowProc。最终,视窗讯息处理程式将获得一个讯息,表示一个功能表项被选择了。通常,这是所有视窗讯息处理程式需要知道的(在第十章将介绍功能表)。

有些Windows程式使用「键盘加速键」来启动通用功能表项。加速键通常是功能键或字母同Ctrl键的组合(例如,Ctrl-S用於保存档案)。这些键盘加速键与程式功能表一起在程式的资源描述档案中定义(我们可以在第十章看到)。Windows将这些键盘加速键转换为功能表命令讯息,您不必自己去进行转换。

对话方块也有键盘介面,但是当对话方块处於活动状态时,应用程式通常不必监视键盘。键盘介面由Windows处理,Windows把关於按键作用的讯息发送给程式。对话方块可以包含用於输入文字的编辑控制项。它们一般是小方框,使用者可以在框中键入字串。Windows处理所有编辑控制项逻辑,并在输入完毕後,将编辑控制项的最终内容传送给程式。关於对话方块的详细资讯,请参见第十一章。

编辑控制项不必局限於单独一行,而且也不限於只在对话方块中。一个在程式主视窗内的多行编辑控制项就能够作为一个简单的文字编辑器了(参见第九、十、十一和十三章的POPPAD程式)。Windows甚至有一个Rich Text文字编辑控制项,允许您编辑和显示格式化的文字(请参见/Platform SDK/User Interface Services/Controls/Rich Edit Controls)。

您将会发现,在开发Windows程式时,可以使用处理键盘和滑鼠输入的子视窗控制项来将较高层的资讯传递回父视窗。只要这样的控制项用得够多,您就不会因处理键盘讯息而烦恼了。

谁获得了焦点
 

与所有的个人电脑硬体一样,键盘必须由在Windows下执行的所有应用程式共用。有些应用程式可能有多个视窗,键盘必须由该应用程式内的所有视窗共用。

回想一下,程式用来从讯息伫列中检索讯息的MSG结构包括hwnd栏位。此栏位指出接收讯息的视窗控制项码。讯息回圈中的DispatchMessage函式向视窗讯息处理程式发送该讯息,此视窗讯息处理程式与需要讯息的视窗相联系。在按下键盘上的键时,只有一个视窗讯息处理程式接收键盘讯息,并且此讯息包括接收讯息的视窗控制项码。

接收特定键盘事件的视窗具有输入焦点。输入焦点的概念与活动视窗的概念很相近。有输入焦点的视窗是活动视窗或活动视窗的衍生视窗(活动视窗的子视窗,或者活动视窗子视窗的子视窗等等)。

通常很容易辨别活动视窗。它通常是顶层视窗-也就是说,它的父视窗代号是NULL。如果活动视窗有标题列,Windows将突出显示标题列。如果活动视窗具有对话方块架(对话方块中很常见的格式)而不是标题列,Windows将突出显示框架。如果活动视窗目前是最小化的,Windows将在工作列中突出显示该项,其显示就像一个按下的按钮。

如果活动视窗有子视窗,那么有输入焦点的视窗既可以是活动视窗也可以是其子视窗。最常见的子视窗有类似以下控制项:出现在对话方块中的下压按钮、单选钮、核取方块、卷动列、编辑方块和清单方块。子视窗不能自己成为活动视窗。只有当它是活动视窗的衍生视窗时,子视窗才能有输入焦点。子视窗控制项一般通过显示一个闪烁的插入符号或虚线来表示它具有输入焦点。

有时输入焦点不在任何视窗中。这种情况发生在所有程式都是最小化的时候。这时,Windows将继续向活动视窗发送键盘讯息,但是这些讯息与发送给非最小化的活动视窗的键盘讯息有不同的形式。

视窗讯息处理程式通过拦截WM_SETFOCUS和WM_KILLFOCUS讯息来判定它的视窗何时拥有输入焦点。WM_SETFOCUS指示视窗正在得到输入焦点,WM_KILLFOCUS表示视窗正在失去输入焦点。我将在本章的後面详细说明这些讯息。

伫列和同步
 

当使用者按下并释放键盘上的键时,Windows和键盘驱动程式将硬体扫描码转换为格式讯息。然而,这些讯息并不保存在讯息伫列中。实际上,Windows在所谓的「系统讯息伫列」中保存这些讯息。系统讯息伫列是独立的讯息伫列,它由Windows维护,用於初步保存使用者从键盘和滑鼠输入的资讯。只有当Windows应用程式处理完前一个使用者输入讯息时,Windows才会从系统讯息伫列中取出下一个讯息,并将其放入应用程式的讯息伫列中。

此过程分为两步:首先在系统讯息伫列中保存讯息,然後将它们放入应用程式的讯息伫列,其原因是需要同步。就像我们刚才所学的,假定接收键盘输入的视窗就是有输入焦点的视窗。使用者的输入速度可能比应用程式处理按键的速度快,并且特定的按键可能会使焦点从一个视窗切换到另一个视窗,後来的按键就输入到了另一个视窗。但如果後来的按键已经记下了目标视窗的位址,并放入了应用程式讯息伫列,那么後来的按键就不能输入到另一个视窗。

按键和字元
 

应用程式从Windows接收的关於键盘事件的讯息可以分为按键和字元两类,这与您看待键盘的两种方式一致。

首先,您可以将键盘看作是键的集合。键盘只有唯一的A键,按下该键是一次按键,释放该键也是一次按键。但是键盘也是能产生可显示字元或控制字元的输入设备。根据Ctrl、 Shift和Caps Lock键的状态,A键能产生几个字元。通常情况下,此字元为小写a。如果按下Shift键或者打开了Caps Lock,则该字元就变成大写A。如果按下了Ctrl,则该字元为Ctrl-A(它在ASCII中有意义,但在Windows中可能是某事件的键盘加速键)。在一些键盘上,A按键之前可能有「死字元键(dead-character key)」或者Shift、Ctrl或者Alt的不同组合,这些组合可以产生带有音调标记的小写或者大写,例如,à、á、a、?、或?。

对产生可显示字元的按键组合,Windows不仅给程式发送按键讯息,而且还发送字元讯息。有些键不产生字元,这些键包括shift键、功能键、游标移动键和特殊字元键如Insert和Delete。对於这些键,Windows只产生按键讯息。

按键讯息
 

当您按下一个键时,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN讯息放入有输入焦点的视窗的讯息伫列;当您释放一个键时,Windows把WM_KEYUP或者WM_SYSKEYUP讯息放入讯息伫列中。

表6-1
键按下键释放非系统键WM_KEYDOWNWM_KEYUP系统键WM_SYSKEYDOWNWM_SYSKEYUP

通常「down(按下)」和「up(放开)」讯息是成对出现的。不过,如果您按住一个键使得自动重复功能生效,那么当该键最後被释放时,Windows会给视窗讯息处理程式发送一系列WM_KEYDOWN(或者WM_SYSKEYDOWN)讯息和一个WM_KEYUP(或者WM_SYSKEYUP)讯息。像所有放入伫列的讯息一样,按键讯息也有时间资讯。通过呼叫GetMessageTime,您可以获得按下或者释放键的相对时间。

系统按键与非系统按键
 

WM_SYSKEYDOWN和WM_SYSKEYUP中的「SYS」代表「系统」,它表示该按键对Windows比对Windows应用程式更加重要。WM_SYSKEYDOWN和WM_SYSKEYUP讯息经常由与Alt相组合的按键产生,这些按键启动程式功能表或者系统功能表上的选项,或者用於切换活动视窗等系统功能(Alt-Tab或者Alt-Esc),也可以用作系统功能表加速键(Alt键与一个功能键相结合,例如Alt-F4用於关闭应用程式)。程式通常忽略WM_SYSKEYUP和WM_SYSKEYDOWN讯息,并将它们传送到DefWindowProc。由於Windows要处理所有Alt键的功能,所以您无需拦截这些讯息。您的视窗讯息处理程式将最後收到关於这些按键结果(如功能表选择)的其他讯息。如果您想在自己的视窗讯息处理程式中加上拦截系统按键的程式码(如本章後面的KEYVIEW1和KEYVIEW2程式所作的那样),那么在处理这些讯息之後再传送到DefWindowProc,Windows就仍然可以将它们用於通常的目的。

但是,请再考虑一下,几乎所有会影响使用者程式视窗的讯息都会先通过使用者视窗讯息处理程式。只有使用者把讯息传送到DefWindowProc,Windows才会对讯息进行处理。例如,如果您将下面几行叙述:

case WM_SYSKEYDOWN:case WM_SYSKEYUP:caseWM_SYSCHAR:    return 0 ;

加入到一个视窗讯息处理程式中,那么当您的程式主视窗拥有输入焦点时,就可以有效地阻止所有Alt键操作(我将在本章的後面讨论WM_SYSCHAR),其中包括Alt-Tab、Alt-Esc以及功能表操作。虽然我怀疑您会这么做,但是,我相信您会感到视窗讯息处理程式的强大功能。

WM_KEYDOWN和WM_KEYUP讯息通常是在按下或者释放不带Alt键的键时产生的,您的程式可以使用或者忽略这些讯息,Windows本身并不处理这些讯息。

对所有四类按键讯息,wParam是虚拟键代码,表示按下或释放的键,而lParam则包含属於按键的其他资料。

虚拟键码
 

虚拟键码保存在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP讯息的wParam参数中。此代码标识按下或释放的键。

哈,又是「虚拟」,您喜欢这个词吗?虚拟指的是假定存在於思想中而不是现实世界中的一些事物,也只有熟练使用DOS组合语言编写应用程式的程式写作者才有可能指出,为什么对Windows键盘处理如此基本的键码是虚拟的而不是真实的。

对於早期的程式写作者来说,真实的键码由实际键盘硬体产生。在Windows文件中将这些键码称为「扫描码(scan codes)」。在IBM相容机种上,扫描码16是Q键,17是W键,18是E、19是R,20是T,21是Y等等。这时您会发现,扫描码是依据键盘的实际布局的。Windows开发者认为这些代码过於与设备相关了,於是他们试图通过定义所谓的虚拟键码,以便经由与装置无关的方式处理键盘。其中一些虚拟键码不能在IBM相容机种上产生,但可能会在其他制造商生产的键盘中找到,或者在未来的键盘上找到。

您使用的大多数虚拟键码的名称在WINUSER.H表头档案中都定义为以VK_开头。表6-2列出了这些名称和数值(十进位和十六进位),以及与虚拟键相对应的IBM相容机种键盘上的键。下表也标出了Windows执行时是否需要这些键。下表还按数位顺序列出了虚拟键码。

前四个虚拟键码中有三个指的是滑鼠键:

表6-2
十进位十六进位WINUSER.H识别字必需?IBM相容键盘101VK_LBUTTON滑鼠左键202VK_RBUTTON滑鼠右键303VK_CANCELˇCtrl-Break404VK_MBUTTON滑鼠中键

您永远都不会从键盘讯息中获得这些滑鼠键代码。在下一章可以看到,我们能够从滑鼠讯息中获得它们。VK_CANCEL代码是一个虚拟键码,它包括同时按下两个键(Ctrl-Break)。Windows应用程式通常不使用此键。

表6-3中的键--Backspace、Tab、Enter、Escape和Spacebar-通常用於Windows程式。不过,Windows一般用字元讯息(而不是键盘讯息)来处理这些键。

表6-3
十进位十六进位WINUSER.H识别字必需?IBM相容键盘808VK_BACKˇBackspace909VK_TABˇTab120CVK_CLEARNum Lock关闭时的数字键盘5130DVK_RETURNˇEnter (或者另一个)1610VK_SHIFTˇShift (或者另一个)1711VK_CONTROLˇCtrl (或者另一个)1812VK_MENUˇAlt (或者另一个)1913VK_PAUSEPause2014VK_CAPITALˇCaps Lock271BVK_ESCAPEˇEsc3220VK_SPACEˇSpacebar

另外,Windows程式通常不需要监视Shift、Ctrl或Alt键的状态。

表6-4列出的前八个码可能是与VK_INSERT和VK_DELETE一起最常用的虚拟键码:

表6-4
十进位十六进位WINUSER.H识别字必需?IBM相容键盘3321VK_PRIORˇPage Up3422VK_NEXTˇPage Down3523VK_ENDˇEnd3624VK_HOMEˇHome3725VK_LEFTˇ左箭头3826VK_UPˇ上箭头3927VK_RIGHTˇ右箭头4028VK_DOWNˇ下箭头4129VK_SELECT422AVK_PRINT432BVK_EXECUTE442CVK_SNAPSHOTPrint Screen452DVK_INSERTˇInsert462EVK_DELETEˇDelete472FVK_HELP

注意,许多名称(例如VK_PRIOR和VK_NEXT)都与键上的标志不同,而且也与卷动列中的识别字不统一。Print Screen键在平时都被Windows应用程式所忽略。Windows本身回应此键时会将视讯显示的点阵图影本存放到剪贴板中。假使有键盘提供了VK_SELECT、VK_PRINT、VK_EXECUTE和VK_HELP,大概也没几个人看过那样的键盘。

Windows也包括在主键盘上的字母和数位键的虚拟键码(数字键盘将单独处理)。

表6-5
十进位十六进位WINUSER.H识别字必需?IBM相容键盘48-5730-39无ˇ主键盘上的0到965-9041-5A无ˇA到Z

注意,数字和字母的虚拟键码是ASCII码。Windows程式几乎从不使用这些虚拟键码;实际上,程式使用的是ASCII码字元的字元讯息。

表6-6所示的代码是由Microsoft Natural Keyboard及其相容键盘产生的:

表6-6
十进位十六进位WINUSER.H识别字必需?IBM相容键盘915BVK_LWIN左Windows键925CVK_RWIN右Windows键935DVK_APPSApplications键

Windows用VK_LWIN和VK_RWIN键打开「开始」功能表或者(在以前的版本中)启动「工作管理员程式」。这两个都可以用於登录或登出Windows(只在Microsoft Windows NT中有效),或者登录或登出网路(在Windows for Applications中)。应用程式能够通过显示辅助资讯或者当成捷径键看待来处理application键。

表6-7所示的代码用於数字键盘上的键(如果有的话):

表6-7
十进位十六进位WINUSER.H识别字必需?IBM相容键盘96-10560-69VK_NUMPAD0到VK_ NUMPAD9NumLock打开时数字键盘上的0到91066AVK_MULTIPLY数字键盘上的*1076BVK_ADD数字键盘上的+1086CVK_SEPARATOR1096DVK_SUBTRACT数字键盘上的-1106EVK_DECIMAL数字键盘上的.1116FVK_DIVIDE数字键盘上的/

最後,虽然多数的键盘都有12个功能键,但Windows只需要10个,而位元旗标却有24个。另外,程式通常用功能键作为键盘加速键,这样,它们通常不处理表6-8所示的按键:

表6-8
十进位十六进位WINUSER.H识别字必需?IBM相容键盘112-12170-79VK_F1到VK_F10ˇ功能键F1到F10122-1357A-87VK_F11到VK_F24功能键F11到F2414490VK_NUMLOCKNum Lock14591VK_SCROLLScroll Lock

另外,还定义了一些其他虚拟键码,但它们只用於非标准键盘上的键,或者通常在大型主机终端机上使用的键。查看/ Platform SDK / User Interface Services / User Input / Virtual-Key Codes,可得到完整的列表。

lParam资讯
 

在四个按键讯息(WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP)中,wParam讯息参数含有上面所讨论的虚拟键码,而lParam讯息参数则含有对了解按键非常有用的其他资讯。lParam的32位分为6个栏位,如图6-1所示。


 

Windows程式开发设计指南(6) 键盘

图6-1 lParam变数的6个按键讯息栏位

重复计数
 

重复计数是该讯息所表示的按键次数,大多数情况下,重复计数设定为1。不过,如果按下一个键之後,您的视窗讯息处理程式不够快,以致不能处理自动重复速率(您可以在「控制台」的「键盘」中进行设定)下的按键讯息,Windows就把几个WM_KEYDOWN或者WM_SYSKEYDOWN讯息组合到单个讯息中,并相应地增加重复计数。WM_KEYUP或WM_SYSKEYUP讯息的重复计数总是为1。

因为重复计数大於1指示按键速率大於您程式的处理能力,所以您也可能想在处理键盘讯息时忽略重复计数。几乎每个人都有文书处理或执行试算表时画面卷过头的经验,因为多余的按键堆满了键盘缓冲区,所以当程式用一些时间来处理每一次按键时,如果忽略您程式中的重复计数,就能够解决此问题。不过,有时可能也会用到重复计数,您应该尝试使用两种方法执行程式,并从中找出一种较好的方法。

OEM扫描码
 

OEM扫描码是由硬体(键盘)产生的代码。这对中古时代的组合语言程式写作者来说应该很熟悉,它是从PC相容机种的ROM BIOS服务中所获得的值(OEM指的是PC的原始设备制造商(Original Equipment Manufacturer)及其与「IBM标准」同步的内容)。在此我们不需要更多的资讯。除非需要依赖实际键盘布局的样貌,不然Windows程式可以忽略掉几乎所有的OEM扫描码资讯,参见第二十二章的程式KBMIDI。

扩充键旗标
 

如果按键结果来自IBM增强键盘的附加键之一,那么扩充键旗标为1(IBM增强型键盘有101或102个键。功能键在键盘顶端,游标移动键从数字键盘中分离出来,但在数字键盘上还保留有游标移动键的功能)。对键盘右端的Alt和Ctrl键,以及不是数字键盘那部分的游标移动键(包括Insert和Delete键)、数字键盘上的斜线(/)和Enter键以及Num Lock键等,此旗标均被设定为1。Windows程式通常忽略扩充键旗标。

内容代码
 

右按键时,假如同时压下ALT键,那么内容代码为1。对WM_SYSKEYUP与WM_SYSKEYDOWN而言,此位元总视为1;而对WM_SYSKEYUP与WM_KEYDOW讯息而言,此位元为0。除了两个之外:

读书人网 >windows

热点推荐