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

news2024/11/27 18:37:25

上一节中我们得知,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.coloradmin.cn/o/1845110.html

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

相关文章

厚膜电阻电路丝网印刷

厚膜丝网印刷 该技术用于需要长寿命、热耐久性、机械强度、导热性、高密度电气互连、低介电损耗等的苛刻应用 特征&#xff1a; 陶瓷标准工艺从前到后的通孔连接 正面和背面的丝网印刷电阻器是标准工艺 金导体可以用金线和/或氧化铝线进行线键合 可焊接金属化&#xff0c;…

《梦醒蝶飞:释放Excel函数与公式的力量》3.5常用数学函数max

3.5 MAX函数 教案主题&#xff1a;Excel中MAX函数的应用 一、定理定义 MAX函数是Excel中的一个内置函数&#xff0c;用于返回一组数值中的最大值。它可以应用于数据集&#xff0c;帮助用户快速识别最大数值&#xff0c;从而进行数据分析和决策。 二、语法结构 MAX函数的基…

国外开源字典集(wordlists)

Assetnote Wordlists Wordlists that are up to date and effective against the most popular technologies on the internet.https://wordlists.assetnote.io/

LInux驱动开发笔记(十)SPI子系统及其驱动

文章目录 前言一、SPI驱动框架二、总线驱动2.1 SPI总线的运行机制2.2 重要数据结构2.2.1 spi_controller2.2.2 spi_driver2.2.3 spi_device2.2.4 spi_transfer2.2.5 spi_message 三、设备驱动的编写3.1 设备树的修改3.2 相关API函数3.2.1 spi_setup( )3.2.2 spi_message_init( …

Docker定位具体占用大量存储的容器

监控告警生产环境的服务器磁盘分区使用率大于90%&#xff0c;进入服务器查看Docker 的 overlay2 存储驱动目录中占用很大&#xff0c;很可能是某个容器一直在打印日志&#xff0c;所以需要定位到是哪个容器&#xff0c;然后进行进一步排查。 然后进入到overlay2中查看是哪个目录…

Python日志管理利器:如何高效管理平台日志

一、为什么需要日志管理&#xff1f; 日志是应用程序的重要组成部分&#xff0c;它记录了应用程序的运行状态、错误信息以及用户交互等关键信息。良好的日志管理可以帮助开发人员及时发现和解决问题&#xff0c;提高应用程序的稳定性和可靠性。 项目在本地开发调试时&#xf…

第一百一十六节 Java 面向对象设计 - Java 终止块

Java 面向对象设计 - Java 终止块 ​try ​块也可以有零个或一个​ finally​ 块。 ​finally ​块总是与 ​try ​块一起使用。 语法 使用 ​finally​ 块的语法是 finally {// Code for finally block }​finally​ 块以关键字 ​finally​ 开始&#xff0c;后面紧跟一对…

qemu 安装ubuntu -纯命令行-可ssh-带网络-可gdb linux kernel

1&#xff0c;预备系统盘数据 1.1 下载光盘 注意需要 liver-server $ wget https://releases.ubuntu.com/22.04.4/ubuntu-22.04.4-live-server-amd64.iso 1.2 挂载并拷贝 $ sudo mkdir /mnt/iso_ubuntu-22.04.4-live-server-amd64 $ sudo mount ubuntu-22.04.4-live-ser…

Linux系统安装Dify结合内网穿透实现远程访问本地LLM开发平台

文章目录 前言1. Docker部署Dify2. 本地访问Dify3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 前言 本文主要介绍如何在Linux Ubuntu系统使用Docker快速部署大语言模型应用开发平台Dify,并结合cpolar内网穿透工具实现公网环境远程访问…

差分总结(一维+二维)

差分&#xff0c;可以视作前缀和的逆运算。 前缀和用于去求一个区间段的和 差分用于改变一个区间的值&#xff08;比如说某个区间都加上或者减去一个数&#xff09; P2367 语文成绩 #include<bits/stdc.h> using namespace std; #define int long long int n,p; int a…

问题解决:Problem exceeding maximum token in azure openai (with java)

问题背景&#xff1a; Im doing a chat that returns queries based on the question you ask it in reference to a specific database. For this I use azure openai and Java in Spring Boot. 我正在开发一个聊天功能&#xff0c;该功能根据您针对特定数据库的提问返回查询…

模型预测控制MPC详解(附带案例实现)

模型预测控制MPC详解&#xff08;附带案例实现&#xff09; 文章目录 模型预测控制MPC详解&#xff08;附带案例实现&#xff09;1. 最优控制问题2. 什么是MPC3. 二次规划Quadratic Programming4. MPC为什么可以转换成QP问题&#xff08;推导过程&#xff09;5. MPC总结5.1 MPC…

HTML静态网页成品作业(HTML+CSS+JS)——家乡莆田介绍网页(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;使用Javacsript代码实现图片轮播&#xff0c;共有5个页面。 二、作品…

卓越的 App UI 风格引领潮流

卓越的 App UI 风格引领潮流

QT基础 - 常见图表绘制

目录 零. 前言 一. 添加模块 折线图 三. 树状图 四. 饼图 五. 堆叠柱状图 六. 百分比柱状图 七. 散点图和光滑曲线图 散点图 光滑曲线图 零. 前言 Qt Charts 是 Qt 框架的一个模块&#xff0c;用于创建各种类型的图表和数据可视化。它为开发者提供了一套功能强大的工…

【面试干货】Java的基础类型和字节大小

【面试干货】Java的基础类型和字节大小 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java编程语言中&#xff0c;有八种基本数据类型&#xff0c;它们分别是&#xff1a;布尔型&#xff08;boolean&#xff09;、字节型&#xff08;byt…

什么是 SSH(安全外壳协议)以及如何工作

安全外壳协议&#xff08;Secure Shell&#xff0c;简称SSH&#xff09;&#xff0c;旨在取代未加密的协议&#xff08;如 Telnet 和 RSH&#xff09;和未受保护的文件传输协议&#xff08;如 FTP 和 RCP&#xff09;&#xff0c;在两个设备之间提供安全的加密连接。 安全外壳…

MyBatis 动态 SQL怎么使用?

引言&#xff1a;在现代的软件开发中&#xff0c;数据库操作是任何应用程序的核心部分之一。而在 Java 开发领域&#xff0c;MyBatis 作为一款优秀的持久层框架&#xff0c;以其简洁的配置和强大的灵活性被广泛应用。动态 SQL 允许开发人员根据不同的条件和场景动态地生成和执行…

Kubernetes部署Kanboard看板管理平台

【云原生】Kubernetes部署Kanboard项目管理平台 文章目录 【云原生】Kubernetes部署Kanboard项目管理平台介绍资源列表基础环境一、检查k8s环境1.1、检查工作节点状态1.2、检查系统pod状态 二、编辑kanboard.yaml文件2.1、创建项目目录2.2、编辑kanboard.yaml文件 三、部署Kanb…

字节跳动的丝绸之路,豆包或将成为赛博“杨枝甘露”

大数据产业创新服务媒体 ——聚焦数据 改变商业 关于字节跳动有一个所有人都知道的秘密&#xff0c;那就是他们正在想方设法地海外扩张&#xff0c;以全球市场来驱动企业的新增长。美国曾经是字节跳动的第二战场&#xff0c;但是随着华盛顿相关法案的出台&#xff0c;在各种不…