《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.rhkb.cn/news/355351.html

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

相关文章

wsl2平台鸿蒙全仓docker编译环境快速创建方法

文章目录 1 文章适用范围&#xff1a;2 WSL环境安装3 镜像迁移非C盘4 Docker环境准备4.1 docker用户组和用户创建4.2 Docker环境配置4.2.1 Ubuntu下安装docker工具4.2.2 鸿蒙Docker环境安装4.2.3 鸿蒙全仓代码拉取编译 5 鸿蒙全仓代码的更新策略6 参考文献7 FAQ7.1 缺头文件xcr…

每天写java到期末考试(6.21)--集合4--练习--6.20

练习1&#xff1a; 正常写集合 bool类 代码&#xff1a; import QM_Fx.Student;import java.util.ArrayList;public class test {public static void main(String[] args) {ArrayList<Student> listnew ArrayList<>();//2.创建学生对象Student s1new Student(&quo…

【面试干货】throw 和 throws 的区别

【面试干货】throw 和 throws 的区别 1、throw1.1 示例 2、throws2.1 示例 3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;throw和throws都与异常处理紧密相关&#xff0c;但它们在使用和含义上有明显的区别。…

【Linux】关于在华为云中开放了端口后仍然无法访问的问题

已在安全组中添加规则: 通过指令: netstat -nltp | head -2 && netstat -nltp | grep 8080 运行结果: 可以看到服务器确实处于监听状态了. 通过指令 telnet 公网ip port 也提示: "正在连接xxx.xx.xx.xxx...无法打开到主机的连接。 在端口 8080: 连接失败"…

[C++][数据结构][B-树][上]详细讲解

目录 0.常见的搜索结构1.B树概念2.B-树的插入分析1.流程分析2.插入过程总结 0.常见的搜索结构 种类数据格式时间复杂度顺序查找无要求 O ( N ) O(N) O(N)二分查找有序 O ( l o g 2 N ) O(log_2 N) O(log2​N)二叉搜索树无要求 O ( N ) O(N) O(N)二叉平衡树无要求 O ( l o g 2 …

Nvidia Isaac Sim搭建仿真环境 入门教程 2024(4)

Nvidia Isaac Sim 入门教程 2024 版权信息 Copyright 2023-2024 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. …

怎么添加网页到桌面快捷方式?

推荐用过最棒的学习网站&#xff01;https://offernow.cn 添加网页到桌面快捷方式&#xff1f; 很简单&#xff0c;仅需要两步&#xff0c;接下来以chrome浏览器为例。 第一步 在想要保存的网页右上角点击设置。 第二步 保存并分享-创建快捷方式&#xff0c;保存到桌面即可…

【Unity】AssetBundle打包策略

【Unity】AssetBundle打包策略 在游戏开发过程中&#xff0c;AssetBundle(AB)打包策略的重要性不容忽视。游戏开发者往往手动设置游戏资源包名进行管理&#xff0c;难免会造成资源确实或导致冗余&#xff0c;因此对于AB包的打包流程来说&#xff0c;进行策略管理显得十分重要。…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] API集群访问频次统计(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

IOS逆向分析—终极详细(三)

IOS逆向分析—终极详细&#xff08;三&#xff09; 前言一、逆向分析是什么&#xff1f;二、IDA分析1.下载并安装IDA2.安装插件3.加载二进制4.代码分析5.其它 总结 前言 本文是个人完成对IOS上APP分析的整个过程&#xff0c;当然对于不同的机型还会遇到不同的情况&#xff0c;谨…

RabbitMQ 开发指南

连接RabbitMQ 连接方式一&#xff1a; 也可以选择使用URI的方式来实现 连接方式二&#xff1a; Connection接口被用来创建一个Channel&#xff0c;在创建之后&#xff0c;Channel可以用来发送或者接收消息。 Channel channel conn.createChannel();使用交换器和队列 声明…

android在线阅读代码网站

android在线阅读代码社区&#xff1a; Android 1.6 到 Android 10 的源码&#xff1a; Android OS 在线源代码 - https://www.androidos.net.cn10.0.0_r6 - Android社区 - https://www.androidos.net.cn/ AndroidXRef https://cs.android.com/ https://cs.android.com/android…

《梦醒蝶飞:释放Excel函数与公式的力量》4.1if函数

第4章&#xff1a;逻辑与条件函数 第一节4.1 if函数 在Excel中&#xff0c;逻辑函数用于处理基于特定条件的真假判断&#xff0c;它们是构建复杂公式和进行高级数据分析的基础。本章将深入探讨逻辑函数的使用方法&#xff0c;特别是IF函数&#xff0c;这是Excel中最为常用的条…

win10免安装配置MySQL8.4.0

注&#xff1a;此教程基于win10 22H2 版本 1、下载最新版本MySQL压缩包 下载链接&#xff1a;MySQL官网下载地址 点击第二行的 ZIP Archive 后面的Download&#xff08;当前时间2024-06-19最新版本是8.4.0&#xff09; 2、解压并添加配置文件 下载完毕后&#xff0c;解压缩…

修复 pprof ---node_exproter访问漏洞(go-pprof-leak)

前言&#xff1a; ** 在Go语言中&#xff0c;pprof和debug包是用来检测和避免goroutine泄漏&#xff0c;避免导致goroutine泄漏&#xff0c;进而消耗大量系统资源。不过对于安全而言确又存在一定风险&#xff0c;** 风险&#xff1a; 通过node_exporter web发现 190.168.46.1…

数据结构之“算法的时间复杂度和空间复杂度”

&#x1f339;个人主页&#x1f339;&#xff1a;喜欢草莓熊的bear &#x1f339;专栏&#x1f339;&#xff1a;数据结构 目录 前言 一、算法效率 1.1算法的复杂度概念 1.2复杂度的重要性 二、时间复杂度 2.1时间复杂度的概念 2.2大O的渐进表示法 2.3常见的时间复杂度…

【人机交互 复习】第1章 人机交互概述

人机交互的知识点碎&#xff0c;而且都是文字&#xff0c;过一遍脑子里什么都留不下&#xff0c;但是背时间已经来不及了&#xff0c;最好还是找题要题感吧&#xff0c;加深印象才是做对文科的关键 一、概念 1.人机交互&#xff08;Human-Computer Interaction,HCI)&#xff1…

Blazor的SSR服务端渲染是不是交互式的

从.NET8开始&#xff0c;Blazor引入了SSR服务端渲染&#xff0c;归功于MVC和RazePage的沉淀&#xff0c;虽然来得晚&#xff0c;但一经发布&#xff0c;就将Blazor推向了新的高度。从今年开始&#xff0c;Youtube上关于Blazor的优质教学视频&#xff0c;以肉眼可见的速度在增加…

【机器学习】机器的登神长阶——AIGC

目录 什么是AIGC 普通用户接触AIGC网站推荐 通义千问 白马 普通用户如何用好AIGC 关键提示词的作用 AIGC的影响 就业市场&#xff1a; 教育领域&#xff1a; 创意产业&#xff1a; 经济活动&#xff1a; 社交媒体与信息传播&#xff1a; AIGC面临的挑战 什么是AIGC…

python离线安装第三方库、及其依赖库(单个安装,非批量移植)

文章目录 1.外网下载第三方库、依赖库2.内网安装第三方库3.补充附录内网中离线安装python第三方库,这时候只能去外网手动下载第三方库,再传回内网进行安装。 问题是python第三方库往往有其前置依赖包,你很难清楚某个第三方库依赖的是哪些依赖包,更难受的是依赖包可能还有其…