《Windows API每日一练》5.2 按键消息

上一节中我们得知,Windows系统的按键消息有很多类型,大部分按键消息都是由Windows系统的默认窗口过程处理的,我们自己只需要处理少数几个按键消息。这一节我们将详细讲述Windows系统的所有按键消息及其处理方式。

本节必须掌握的知识点:

        系统按键消息和非系统按键消息

        虚拟键码

        lParam信息

        转移状态

        使用按键消息

        第30练:滚动条的键盘接口

5.2.1 系统按键消息和非系统按键消息

       ■按键消息的分类      

键按下

键释放

非系统按键消息

WM_KEYDOWN

WM_KEYUP

系统按键消息

WM_SYSKEYDOWN

WM_SYSKEYUP

表5-1 按键消息

      

非系统按键消息

当我们按下一个键盘按键时,会产生一个WM_KEYDOWN消息,松开按键时,同样也会产生一个按键消息WM_KEYUP。Windows系统会将这两个按键消息送入具有输入焦点的窗口消息队列。通常键按下消息和键释放消息是成对出现的。但是如果你按下一个键不放时,则被认为发生了一次连续按键(自动重复)行为,Windows将发送给窗口过程一连串的 WM_KCEYDOWN(焦点窗口最小化时为WM_SYSKEYDOWN)消息。当此键最终被释放时,Windows发送给窗口过程一个WM_KEYUP(焦点窗口最小化时为WM_SYSKEYUP)消息。像所有的队列消息一样,击键消息是可被实时追踪的。你能通过调用GetMessageTime函数得到键被按下或释放的相对时间。

系统按键消息

WM_SYSKEYDOWN和WM_SYSKEYUP中的“SYS”代表系统,它表明该击键对 Windows比对Windows应用程序更加重要。当输入键和Alt键组合时通常产生的是 WM_SYSKEYDOWN和WM_SYSKEYUP消息。这些按键调用程序菜单或系统菜单选项,被用来实现系统功能如转换活动窗口(Alt-Tab键或Alt-Esc键),或作为系统菜单快捷键(Alt 键和功能键的组合,如Alt-F4是用于关闭一个应用程序)。应用程序通常忽略 WM_SYSKEYUP和WM_SYSKEYDOWN消息,将它们交付给DefWindowProc函数完成默认处理。因为Windows关注所有的Alt键功能逻辑,应用程序就不必处理这些消息。你的窗口过程最终会接收到的是与击键产生结果相关的消息(如一个菜单被选中)。如果你在窗口过程中代码去捕获这些系统击键消息(就像在本章稍后将介绍的KEYVIEW1和 KEYVIEW2程序中实现的那样),则在处理完毕后,仍然需要发送这些消息给 DefWindowProc函数,以便不影响Windows对它的处理。

       【注意】被拦截的系统消息窗口过程处理后,仍然需要交给Windows默认的窗口过程DefWindowProc函数处理,否则将会打断系统消息的传递流程,导致程序错误。

当然如果我们确实想要可以屏蔽所有系统消息,则可以在拦截系统消息后直接返回。可以在窗口过程中添加如下代码:

case WM_SYSKEYDOWN:

case VIM_SYSREYUP:

case WM_SYSCHAR:

              return 0 ;

那么在你的程序主窗口具有输入焦点时,就可以有效地阻止所有Alt键的操作。 (WM_SYSCHAR消息将在本章稍后的部分讨论。)这些操作包括Alt-Tab键、Alt-Esc键和菜单操作。虽然你不一定想做这些,但我相信你能感觉到窗口过程内含的强大功能。

不与Alt组合时按下和释放键会产生WM_KEYDOWN和WM_KEYUP消息。应用程序可以使用或者丢弃这些击键消息。Windows也不处理它们。

对所有四类击键消息,wParam是虚拟键代码,用于标识哪个键被按下或被释放,而 IParam包含属于本次击键的一些其他数据。

5.2.2 虚拟键码

虚拟键码

虚拟键码(Virtual Key Codes)是用于表示键盘上的按键的整数值。在Windows操作系统中,每个按键都被分配了一个唯一的虚拟键码。

虚拟键码由VK_前缀和一个标识符组成,例如VK_A表示字母A键的虚拟键码。

虚拟键码在编程中常用于处理键盘输入。您可以通过捕捉键盘事件并检查事件中的虚拟键码来确定哪个按键被按下或释放。

【注意】虚拟键码是特定于操作系统的。不同的操作系统可能会使用不同的虚拟键码值。上述示例是针对Windows操作系统的常见虚拟键码。

wParam参数

    虚拟键代码存储在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和 WM_SYSKEYUP消息的wParam参数中。此代码确定哪个键被按下或被释放。

如果你学习过DOS系统16位汇编语言,一定知道键盘扫描码。键盘上的每一个按键都有唯一一个与此对应的扫描码。在IBM兼容键盘上,扫描码16为Q键,17为W键,18为E键,19为R键,20为T 键,21为Y键等。

到了Windows操作系统时代,由于Windows操作系统需要支持全世界几乎所有的语言文字和字符,不同语言版本的Windows操作系统使用的键盘上的字符是不一样的。因此,Windows系统需要支持的“扫描码”要比早期的DOS系统多的多,而且还需要为未来键盘可能需要支持的按键做预留。Windows系统使用了一套与设备无关的方式来处理键盘。至此,我们应该可以理解虚拟键码的真实含义。

大多数虚拟键代码命名是以VK_开头的,它定义在WINUSER.H头文件中。下面这些表中列出了这些虚拟键代码的名称和数值(用十进制和十六进制)以及对应于虚拟键的IBM兼容键盘上的键。同时也指出了哪些键是Windows正常运转中所需要用到的。这些表以十进制顺序列出虚拟键代码。

前四个虚拟键代码中的三个涉及鼠标按钮。

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

1

01

VK_LBUTTON

鼠标左键

2

02

VK_RBUTTON

鼠标右键

3

03

VK_CANCEL

Ctrl-Break

4

04

VK_MBUTTON

鼠标中键

VK_CANCEL码是唯一的标识同时按下两个键(Ctrl+Break)的虚拟代码。 Windows应用程序通常不使用此键。

【注意】鼠标按键虚拟键码并不会出现在键盘消息中,而是在鼠标消息中。第六章我们将讲述鼠标消息。

以下表中的一些键,如退格键、Tab键、回车键、Esc键和空格键,经常被用于Windows

程序中。但是Windows程序通常使用字符消息(而不是击键消息)来处理这些键。Windows应用程序通常不必去监视Shift键、Ctrl键或Alt键的状态。

      

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

8

08

VK_BACK

退格键

9

09

VK_TAB

Tab键

12

0C

VK_CLEAR

清除键

13

0D

VK_RETURN

回车键(任意)

16

10

VK_SHIFT

Shift键(任意)

17

11

VK_CONTROL

Ctrl键(任意)

18

12

VK_MENU

Alt键(任意)

19

13

VK_PAUSE

Pause键

20

14

VK_CAPITAL

大写锁定键

27

1B

VK_ESCAPE

Esc键

32

20

VK_SPACE

空格键

下表中列出的前八个代码以及VK_INSERT、VK_DELETE码可能是最常使用的虚拟键代码:

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

33

21

VK_PRIOR

PageUp键

34

22

VK_NEXT

PageDown键

35

23

VK_END

End键

36

24

VK_HOME

HOME键

37

25

VK_LEFT

向左箭头键

38

26

VK_UP

向上箭头键

39

27

VK_RIGHT

向右箭头键

40

28

VK_DOWN

向下箭头键

41

29

VK_SELECT

42

2A

VK_PRINT

43

2B

VK_EXCUTE

44

2C

VK_SNAPSHOT

PrintScreen键

45

2D

VK_INSERT

Insert键

46

2E

VK_DELETE

Del键

47

2F

VK_HELP

假想键

Windows也包含了主键盘上的字母键和数字键的虚拟键代码(数字键盘被单独处理):      

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

48-57

30-39

主键盘0-9

65-90

41-5A

A-Z

【注意】数字键和字母键的虚拟键代码就是ASCII码。Windows程序几乎从来不用这些虚拟键代码,相反这些程序依赖于ASCII字符表示的字符消息。

下面的键是由微软Natural Keyboard键盘及其兼容键盘产生的。      

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

91

5B

VK_LWIN

左Win键

9

5C

VK_RWIN

右Win键

93

5D

VK_APPS

Application键

VK_LWIN和VK_RWIN键被Windows用于打开开始菜单或(在较早的版本中)启动任务管理器。它们也能用于登录或注销Windows(仅在Microsoft Windows NT中),或者是登录或注销网络(用于Windows的工作组版本)。应用程序能通过显示帮助信息或快捷键来处理 Application 键。

下面的代码是和数字小键盘中的键相对应的代码(如果存在的话):      

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

96-105

60-69

VK_NUMPAD0-

VK_NUMPAD9

NumLock打开时

数字键区0~9

106

6A

VK_MULTIPLY

数字键区*

107

6B

VK_ADD

数字键区+

108

6C

VK_SEPARATOR

109

6D

VK_SUBTRACT

数字键区-

110

6E

VK_DECIMAL

数字键区.

111

6F

VK_DIVIDE

数字键区/

尽管大部分键盘都有12个功能键,Windows则仅需要10个(F11、F12除外),但它却有24个数字标识符。此外,程序通常把功能键用作键盘快捷键,所以它们通常不处理下表中的击键:

十进制

十六进制

WINUSER.H中的标识符

是否必需

IBM兼容键盘

112-121

70-79

VK_F1-VK_F10

功能键F1到F10

122-135

7A-87

VK_F11-VK_F24

功能键F11-F24

144

90

VK_NUMLOCK

数字锁定键

145

91

VK_SCROLL

Scroll Lock键

虽然还定义了其他一些虚拟键代码,但它们被保留为非标准键盘上的键或者主机终端 上的键。有兴趣的读者可以自行查阅相关资料,这里不再阐述。

5.2.3 lParam信息

如前所述,在四个按键消息中(WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、 WM_SYSKEYUP),wParam消息参数包含了虚拟键代码,IParam消息参数包含了帮助理解击键的其他有用信息。32位的lParam消息被分成了 6个字段,如图5-2所示。

图5-2 lParam参数的6个按键消息字段

重复计数

重复计数是消息所表示的击键的数目。大多数情况下,它被设置为1。但是,如果你按下一个键不放,且窗口过程不足够快,跟不上输入速率(该项可在控制面板的【键盘】应用程序中设置)来处理击键消息,Windows就会把一些WM_KEYDOWN和 WM_SYSKEYDOWN消息合并成一个单独的消息,并相应增加重复计数字段。WM_KEYUP 和WM_SYSKEYUP消息的重复计数总是为1。

重复计数大于1表明此时连续击键的速度快于程序的处理能力,所以你可能想在处理键盘消息的时候忽略重复计数。由于额外的击键堆积,几乎每一个人都有过字处理文档或电子表格不停滚屏的经历。当程序要花费一段时间来处理每一个击键时,应用程序可以忽略重复计数来解决此问题。但是在其他情况下,你也许需要使用重复计数。你可能需要在这两种情况下执行程序,找到最合适的一种。

OEM扫描码

OEM扫描码是键盘硬件产生的代码。这对中年的汇编语言程序员来说是相当熟悉的, 他们从PC兼容机的ROM BIOS服务中获得这些值(OEM指的是个人计算机的原始设备制造厂商(Original Equipment Manufacturer),在这里是指“IBM标准”)我们不再需要这种东西了。Windows程序几乎可以做到忽略OEM扫描码,除非是它要依赖于键盘上键的分布。

扩展键标记

如果击键结果来自于IBM加强型键盘的附加键,则扩展键标记为1。(IBM加强型键盘 有101或102个键。键盘上部是功能键。光标移动键与数字小键盘分离,但数字小键盘保留有光标移动键的功能。)键盘右侧的Alt和Ctrl键、分离于数字小键盘的光标移动键(包含 Insert键和Delete键)、数字小键盘的斜线和回车键,以及NumLock键的这一标记位均设置为1。Windows程序通常忽略扩展键标记。

内容代码

如果在击键的同时也按下了Alt键,则内容代码为1。WM_SYSKEYUP和 WM_SYSKEYDOWN消息的此位始终为1,而WM_KEYUP和WM_KEYDOWN消息的此位始终为0。有两种情况例外。

●如果活动窗口最小化了,则它不具有输入焦点。所有的击键将产生 WM_SYSKEYUP和WM_SYSKEYDOWN消息。如果Alt键未被按下,内容代码 字段将被置为 0。Windows 处理 WM_SYSKEYUP 和 WM_SYSKEYDOWN 消息, 使最小化的活动窗口不处理这些击键。

●在某些非英语的键盘上,一些字符是通过Shift键、Ctrl键或Alt键同另一个键的组合产生的。在这些情况下,内容代码被设置为1,但消息并不是系统击键消息。

键的先前状态

如果键以前是处于释放状态的,则键的先前状态为0。而如果键以前是按下的,则键的先前状态为1。WM_KEYUP和WM_SYSKEYUP消息的此字段总是为1。但 WM_KEYDOWN和WM_SYSKEYDOWN消息的此字段可能为0或1。该位为1表明,消息为重复击键产生的第二个或后续发出的消息。

转换状态

如果键正在被按下,转换状态为0。如果键正在被释放,转换状态为1。WM_KEYDOWN和WM_SYSKEYDOWN消息的此字段设置为0,而WM_KEYUP和WM_SYSKEYUP消息的此字段设置为1。

5.2.4 转义状态

GetKeyState函数

当处理击键消息时,你可能需要知道是否有转义键(Shift键、Ctrl键和Alt键)或切换键 (CapsLock键、Num Lock键和Scroll Lock键)被按下。你能通过调用GetKeyState函数获得此信息。例如:

iState = GetKeyState(VK_SHIFT);

如果Shift键被按下,则iState变量为负(即高位置1)。如果CapsLock键打开,则从

iState = GetKeystate(VK_CAPITAL);

返回的值最低位置为1。此位与键盘上的小灯保持一致。

GetKeystate函数原型如下:

SHORT GetKeyState(

  int nVirtKey       //表示要检索状态的虚拟键的虚拟键码

);

函数返回一个SHORT类型的值,表示指定虚拟键的状态。如果返回值的最高位(位15)为1,则表示该键当前按下;如果最高位为0,则表示该键当前释放。

通常你会使用虚拟键代码VK_SHIFT、VK_CONTROL和VK_MENU(你也许还记得指 Alt键)来调用GetKeyState函数。你也能用GetKeyState函数通过标识符VK_LSHIFT、 VK_RSHIFT、VK_LCONTROL、VK_RCONTROL> VK_LMENU 或 VK_RMENU 来确定是左侧还是右侧的Shift键、Ctrl键或Alt键被按下。这些标识符仅在GetKeyState函数和 GetAsyncKeyState函数中使用(下面将详细介绍)。

你也能使用虚拟键代码VK_LBUTTON、VK_RBUTTON和VK_MBUTTON来得到鼠标按钮的状态。但是,大多数需要监视鼠标按钮和击键的Windows 程序通常使用另一种方法,即当Windows程序接收到鼠标消息时,才检查击键。实际上,转义状态信息被包含在鼠标消息中,我们将在下一章介绍。

【注意】GetKeyState函数的用法。它并非实时地检査键盘状态。更准确地说,它反映了 到目前为止的键盘状态,并包含了正在被处理的当前消息。大多数情况下,这正是你想要的。如果你需要确定用户是否按下了 Shift+Tab键,可在处理Tab键的WM_KEYDOWN消息时,调用含VK_SHIFT参数的GetKeyState函数。如果GetKeyState函数的返回值是负的,你就知道在按下Tab键之前按下了Shift键。并且在你处理Tab键时,Shift键是否己被释放没有什么影响。你只要知道在Tab键按下的时候,Shift键是按下的。

GetKeyState函数无法让你获得独立于标准键盘消息的键盘信息。例如,你也许感到有 必要暂停窗口过程的处理,直到用户按下F1功能键:

while (GetKeyState(VK_F1) >= 0); // WRONG !!!

这种做法是错误的!这一定会中止你的程序(当然,除非在执行该语句之前,你从消息队列中获得了F1功能键的WM_KEYDOWN消息)。如果你确实需要了解某个键的当前实时状态,可以使用GetAsyncKeyState函数。

GetAsyncKeyState函数

GetAsyncKeyState函数用于检索指定虚拟键的状态,包括当前是否按下和之前是否按下。

函数原型如下:

SHORT GetAsyncKeyState(

  int vKey      //表示要检索状态的虚拟键的虚拟键码

);

       函数返回一个SHORT类型的值,表示指定虚拟键的状态。返回值的最高位(位15)表示键的当前状态,如果最高位为1,则表示该键当前按下;如果最高位为0,则表示该键当前释放。返回值的第二高位(位14)表示键的之前状态,如果第二高位为1,则表示该键之前被按下;如果第二高位为0,则表示该键之前被释放。

GetKeyState函数与GetAsyncKeyState函数的区别

GetKeyState函数和GetAsyncKeyState函数都用于检索虚拟键的状态,但它们之间存在一些区别。

●返回值的含义不同:

GetKeyState函数的返回值是一个SHORT类型的值,其中最高位(位15)表示键的当前状态(按下或释放),第二高位(位14)表示键的之前状态。这种返回值的结构使得可以同时获取键的当前状态和之前状态。

GetAsyncKeyState函数的返回值也是一个SHORT类型的值,其中最高位(位15)表示键的当前状态(按下或释放),但没有直接提供之前状态的信息。

●作用范围不同:

GetKeyState函数获取的是当前线程的键盘状态。它返回的是当前线程内最近一次按键的状态,不考虑其他线程或应用程序的按键状态。

GetAsyncKeyState函数获取的是全局的键盘状态。它可以用于检测其他应用程序或窗口中的按键状态。

●对重复按键的处理不同:

GetKeyState函数可以通过返回值中的重复计数字段(位0-15)指示按键是否为重复按下。重复计数为1表示按键是刚刚按下的,重复计数大于1表示按键是重复按下的。

GetAsyncKeyState函数不提供直接的重复计数信息。如果需要处理重复按键,可以在代码中使用额外的逻辑来跟踪按键状态的变化。

5.2.5 使用按键消息

Windows程序忽略了大部分的按键消息,只是处理一些少数按键消息。Windows系统默认窗口过程函数处理WM_SYSKEYDOWN和WM_SYSKEYUP消息,应用程序不必关心它们。如果应用程序处理WM_KEYDOWN消息,通常可以忽略WM_KEYUP消息。

Windows程序通常为不产生字符的击键使用WM_KEYDOWN消息。尽管你认为有可能可以通过使用按键消息和转义状态信息,把击键消息转换为字符,但也不要这么做。你将会在非英语键盘上遇到问题。例如,如果你获得wParam参数等于0x33的 WM_KEYDOWN消息,你知道用户按下了数字键3。到目前为止,一切都还不错。如果你使用GetKeyState函数,且发现Shift键被按下,你也许会认为用户正在输入“#”。未必如此,例如英国用户就是在输入另一种符号,看起来像£。

对光标移动键、功能键、Insert键和Delete键,WM_KEYDOWN消息是最有用的。但是 Insert键、Delete键与功能键,经常被用作菜单快捷键。因为Windows会把菜单快捷键转换为菜单命令消息,所以应用程序也不必自己处理这些按键。

Windows之前的MS-DOS应用程序曾经大量地使用功能键与Shift键、Ctrl键和Alt键 的组合。你能在Windows程序中做类似的事情(的确,Microsoft Word大最地使用了功能键作为快捷命令方式),但不推荐这么做。如果你确实想使用功能键,这些功能键应该重复菜单命令。Windows的目标之一就是提供不需要记忆或查询复杂命令表的用户界面。

因此,总结如下:大部分时间,你仅需要处理光标移动键的WM_KEYDOWN消息,有时处理Insert键和Delete键的WM_KEYDOWN消息。当使用这些键时,可以通过 GetKeyState函数检查 Shift键和Ctrl键的状态。例如,Windows程序经常使用Shift键和光标键的组合来扩大字处理文档中的选中范围。Ctrl键常用于改变光标键的意义。例如,Ctrl 键和右箭头键的组合用于将光标右移一个单词。

决定如何在你的应用程序中使用键盘的一种最好方法是遵循用户的习惯。

5.2.6 第30练:滚动条的键盘接口

/*---------------------------------------------------------

SYSMETS.H -- 系统配置信息结构数组(略)

-----------------------------------------------------------*/

/*------------------------------------------------------------------

030  WIN32 API 每日一练

     第30个例子:滚动条的键盘接口

     SendMessage函数

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

#include "sysmets.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    PSTR szCmdLine, int iCmdShow)

{

    static TCHAR szAppName[] = TEXT("SysMets4");

    (略)

    return msg.wParam;

}

//窗口过程

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

    static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth;

    HDC hdc;

    int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;

    PAINTSTRUCT ps;

    SCROLLINFO si;      //滚动条参数结构变量

    TCHAR szBuffer[10];

    TEXTMETRIC tm;

    switch (message)

    {

    case WM_CREATE:

        return 0;

    case WM_SIZE:

       

        return 0;

    case WM_VSCROLL:

       

        return 0;

    case WM_HSCROLL:

       

        return 0;

        //按键消息

    case WM_KEYDOWN:

        //wParam 指定非系统键的虚拟键码,

        //lParam 指定重复次数,扫描码,扩展键标识符,上下文代码,

//前一键状态标识符,以及转换状态标识符。

        switch (wParam)

        {

        case VK_HOME://HOME

            SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);//发送滚动条值

            break;

        case VK_END://END

            SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);

            break;

        case VK_PRIOR://PageUp

            SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);

            break;

        case VK_NEXT://PageDown

            SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);

            break;

        case VK_UP://上箭头键

            SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);

            break;

        case VK_DOWN://下箭头键

            SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);

            break;

        case VK_LEFT://左箭头键

            SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0);

            break;

        case VK_RIGHT://右箭头键

            SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0);

            break;

        }

        return 0;

    case WM_PAINT:

       

        return 0;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    }

    return DefWindowProc(hwnd, message, wParam, lParam);

}

/******************************************************************************

SendMessage函数:将指定的消息发送到一个或多个窗口。

该SendMessage函数的函数调用指定的窗口的窗口过程,并不会返回,直到窗口过程已经处理了该消息。

要发送消息并立即返回,请使用SendMessageCallback或SendNotifyMessage函数。

要将消息发布到线程的消息队列中并立即返回,请使用PostMessage或PostThreadMessage函数。

LRESULT SendMessage(

  HWND   hWnd, //窗口的句柄,其窗口过程将接收到该消息。

  UINT   Msg,  //要发送的消息。

  WPARAM wParam,//其他特定于消息的信息。

  LPARAM lParam//其他特定于消息的信息。

);

*/

运行结果:

图5-3 滚动条的键盘接口

 

总结

第三章SYSMETS程序的3个版本都是在不了解键盘的情况下写的。我们只能通过在滚动条上使用鼠标来滚动文本。上述实例给程序添加键盘接口。

创建键盘接口的一个简单方法是在窗口过程中增加WM_KEYDOWN逻辑,把每一个WM_KEYDOWN消息转换为等同的WM_VSCROLL或 WM_HSCROLL消息,然后调用SendMessage函数将WM_VSCROLL或 WM_HSCROLL消息直接发送给窗口过程。

SendMessage函数用于向指定的窗口发送一个消息,并等待接收方处理完消息后返回。

函数原型如下:

       LRESULT SendMessage(

  HWND   hWnd,      //要接收消息的窗口的句柄

  UINT   Msg,            //要发送的消息类型(消息ID)

  WPARAM wParam,   //消息的附加参数,具体的含义取决于消息类型

  LPARAM lParam      

);

函数返回一个LRESULT类型的值,表示接收方处理完消息后的返回值。返回值的具体含义取决于发送的消息类型。

我们调用SendMessage函数,将消息发送给指定的窗口。接收方处理完消息后,SendMessage函数会返回接收方的处理结果,我们可以根据返回值进行相应的处理。

需要注意的是,SendMessage函数是同步的,即在消息发送的过程中,发送方会等待接收方处理完消息后才返回。这可能会导致阻塞发送方的线程,直到接收方处理完消息。如果不希望发送方被阻塞,可以考虑使用PostMessage函数发送异步消息。

下面将说明在SYSMETS程序中,我们怎样使用SendMessage函数处理 WM_KEYDOWN 消息:

case WM_KEYDOWN:

       switch (wParam)

       {

       case VK_HOME: //HOME键转换为WM_VSCROLL消息的SB_TOP

              SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0) ;

              break ;

       case VK_END: //HOME键转换为WM_VSCROLL消息的SB_BOTTOM

              SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0) ;

              break ;

       case VK_PRIOR: //HOME键转换为WM_VSCROLL消息的SB_PAGEUP

              SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0) ;

              break ;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/31229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决IDEA使用卡顿的问题,设置JVM内存大小和清理缓存

解决IntelliJ IDEA中卡顿问题&#xff0c;可以尝试以下几个常见且有效的步骤&#xff1a; 1 增加IDEA的JVM内存分配&#xff1a; 位于IDEA安装目录的bin文件夹下&#xff0c;找到对应的操作系统配置文件&#xff08;idea64.exe.vmoptions&#xff08;Windows&#xff09;或id…

BFS:FloodFill算法

文章目录 FloodFill算法简介1.图像渲染2.岛屿数量3.岛屿的最大面积4.被围绕的区域总结 FloodFill算法简介 Flood Fill算法是一种用于确定与某个给定节点相连的区域的算法&#xff0c;常用于计算机图形学和图像处理。该算法可以用于诸如填充多边形、检测连通区域等任务。Flood …

做电池研究如何发表Nature Communications,案例分析

✨【元素魔方学术俱乐部】✨ &#x1f469;‍&#x1f3eb;&#x1f468;‍&#x1f3eb;我们创建了一个学术交流群 给全国各地以及各种研究方向的硕博 和老师们提供一个交流的平台&#x1f4da;&#x1f9ea; 感兴趣的话欢迎加入 &#x1f4f2;本公众号中回复“社群” 会自动发…

Lynred在欧洲防务展上将展出新品——“HOT”红外传感器Seegnus。

Lynred在即将举办的巴黎欧洲防务展上将展出其令人瞩目的新品——“HOT”红外传感器Seegnus。这款专为战术视觉设计的大型阵列传感器&#xff0c;以其紧凑的封装和高分辨率的中波红外成像能力&#xff0c;无疑将为航空航天、国防和商业市场带来革命性的突破。 Seegnus传感器拥有…

RX数据集成:信创生态下的平滑过渡方案

过去&#xff0c;众多中国企业倾向于采用国际供应商的数据集成产品与方案。其中Informatica作为行业翘楚&#xff0c;以其卓越性能和技术领先地位赢得了全球500强中95%企业的青睐。在中国市场上&#xff0c;众多企业同样信赖并采纳其解决方案。然而&#xff0c;随着国际环境的演…

威泰视信嵌入式软件工程师笔试题

威泰视信嵌入式软件工程师笔试题答题时间 45分钟 选择题 1、以下不需要编译内核的情况是 A、删除系统不用的设备驱动程序时 B、升级内核时 C、添加新硬件时 D、激活网卡 只要你代码或配置有改动&#xff0c;都要重新编译的&#xff0c;只是如果代码写成块模式的&#xff…

【LLM之RAG】RAT论文阅读笔记

研究背景 近年来&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在各种自然语言推理任务上取得了显著进展&#xff0c;尤其是在结合大规模模型和复杂提示策略&#xff08;如链式思维提示&#xff08;CoT&#xff09;&#xff09;时。然而&#xff0c;LLMs 在推理的事实…

.net8 blazor auto模式很爽(三)用.net8的Blazor自动模式测试,到底在运行server还是WebAssembly

Blazor自动模式到底什么时侯在运行server&#xff0c;什么时侯在运行WebAssembly。这个对我们来说非常重要&#xff0c;官方并没有很清楚地告诉我们。并且存在一些误导&#xff0c;让我们觉得自动模式就是不管我怎么弄&#xff0c;blazor都会自动识别该使用server还是WebAssemb…

Ubuntu 安装 CloudCompare

步骤&#xff1a; sudo apt install flatpakflatpak install flathub org.cloudcompare.CloudCompare此时会有报错&#xff1a; error: No remote refs found similar to ‘flathub’执行 flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.fla…

【IDEA】Spring项目build失败

通常因为环境不匹配需要在file->projectstructure里面调整一下。

详解DAC数模转换+DAC输出模拟电压的测量比对实验程序

前言&#xff1a;详解DAC数模转换原理DAC输出模拟电压的测量比对实验程序&#xff08;使用 DAC 通道 1 输出模拟电压&#xff0c;然后通过 ADC1 的通道 1 对该输出电压进行读取&#xff0c;并显示在 LCD 模块上面&#xff0c;DAC 的输出电压可以通过按键&#xff08;或 USMART&…

了解CDN:提升网络性能和安全性的利器

在当今的数字时代&#xff0c;网站性能和安全性是每一个网站管理员必须关注的核心问题。内容分发网络&#xff08;CDN&#xff0c;Content Delivery Network&#xff09;作为解决这一问题的重要工具&#xff0c;逐渐成为主流。本文将详细介绍CDN的定义、作用及其工作原理&#…

小白学react之Next.js 14(一)不配置路由的玩法

Next.js 14是目前最新版本&#xff0c;我们用就用最新的玩一下。 建一个示例之后&#xff0c;我在找配置&#xff0c;我应该在那建一个新的页面。找半天硬是没找着&#xff0c;答案是现在不需要配置。 我们来看一下Next.js 14的项目结构&#xff1a; 很明显&#xff0c;在src/…

数学建模系列(4/4):Matlab建模实战

目录 引言 1. Matlab简介与安装 1.1 Matlab简介 1.2 Matlab的安装 2. Matlab基础操作 2.1 Matlab基础语法和常用命令 2.2 Matlab中的数据类型和数据结构 3. 用Matlab进行建模 3.1 矩阵运算与线性代数 矩阵运算 3.2 Matlab中的绘图功能 绘制2D图形 绘制3D图形 3.3…

物联网技术-第5章-物联网数据处理

目录 1.物联网数据特征 2.物联网数据处理 &#xff08;1&#xff09;数据清洗 &#xff08;2&#xff09;数据存储 &#xff08;3&#xff09;数据融合 &#xff08;4&#xff09;数据挖掘 3.大数据基本概念 4.云计算基本概念 &#xff08;1&#xff09;背景 &#xf…

RTD 基础知识——电阻温度检测器简介

电阻温度检测器或 RTD 可能是简单的温度传感器类型。这些设备的工作原理是金属的电阻随温度变化。纯金属通常具有正的电阻温度系数&#xff0c;这意味着它们的电阻随温度升高而增加。RTD 可在 -200 C 至 850 C 的较大温度范围内工作&#xff0c;并提供高精度、出色的长期稳定性…

HoVer-Net复现:手把手带你实现细胞核的分割与分类,并输出叠加图像|24-06-21

小罗碎碎念 先说一下&#xff0c;只要你跟着我一步一步走&#xff0c;你能实现的效果——对细胞核进行分割和分类&#xff0c;并在原始图像上以颜色叠加的方式直观地展示这些结果。 昨天我在交流群里进行了一下预热&#xff0c;并且提供了一些前期的教程&#xff0c;反响还不…

C# 实现去除多行文本框光标闪烁,并设置行距

一、前言 本篇主要通过继承RichTextBox 的方式实现去除多行文本框的光标闪烁&#xff0c;以及能够设置行距大小&#xff0c;这是因为C#提供的TextBox 和 RichTextBox 本身无这样的功能 二、代码 封装 RichTextBox 为CustomTextBox using System; using System.Collections.Ge…

解决element-plus没有导出的成员FormInstance

使用element-plus的el-form时&#xff0c;报错“"element-plus"”没有导出的成员“FormInstance”。你是否指的是“FooterInstance”? 解决方法&#xff1a; 引入ElForm类型&#xff0c;在外重新定义FormInstance的类型为ElForm的实例类型 示例&#xff1a; import…

React+TS前台项目实战(十四)-- 响应式头部导航+切换语言相关组件封装

文章目录 前言Header头部相关组件1. 功能分析2. 相关组件代码详细注释3. 使用方式4. Gif图效果展示 总结 前言 在这篇博客中&#xff0c;我们将封装一个头部组件&#xff0c;根据不同设备类型来显示不同的导航菜单&#xff0c;会继续使用 React hooks 和styled-components库来…