2.3 Windows消息机制 windows ui消息机制
liebian365 2024-10-26 12:59 28 浏览 0 评论
Windows程序的消息机制是指在Windows操作系统下,应用程序与操作系统之间的一种通信方式。通过消息机制,应用程序可以接收来自操作系统的各种事件和请求,以便做出相应的响应和处理。
在Windows程序中,消息机制的实现是基于消息队列和消息循环的。消息队列是一个用于存储消息的缓冲区,当操作系统有消息需要传递给应用程序时,消息会被放入消息队列中。消息循环是应用程序的主循环,在消息循环的过程中,应用程序从消息队列中取出消息并进行处理。
Windows程序中的各种事件和请求都经过消息机制进行传递,比如鼠标点击、键盘输入、窗口尺寸改变等等。当发生这些事件时,操作系统会将相应的消息放入消息队列中,并通知应用程序有新的消息到达。应用程序在消息循环中不断地检查消息队列,处理队列中的消息,直到队列为空或者收到退出消息。
应用程序可以通过消息处理函数来响应不同类型的消息。当该类型的消息到达时,消息处理函数就会被调用,可以在该函数中编写相应的逻辑来处理消息。消息处理函数可以是在创建窗口时指定的窗口过程函数,或者是通过注册消息回调函数的方式来实现。绝大多数消息都是由Windwos系统默认的窗口过程来处理的。
本节必须掌握的知识点:
消息队列
消息循环
2.3.1 消息队列
Windows程序中的消息队列是属于线程的。每个线程初始化时都可以创建一个线程所属的消息队列。但通常在Windows程序中,只有负责处理窗口消息的主线程创建消息队列,因此,我们将线程消息队列也称之为窗口消息队列。通常Windows程序将与用户交互的消息送入消息队列进行同步。例如鼠标消息、键盘消息、绘图消息等。
此外,一些不需要同步的消息会直接分发给窗口过程进行处理,不进入消息队列。
■Windows系统消息队列
Windows操作系统是多任务系统,通常同时有多个窗口程序在运行。所有进程的消息都会被送入Windows系统内的总消息队列。然后再将总消息队列中的消息按照所属的窗口分发给每个进程的窗口消息队列。这就需要判断消息应该属于哪个窗口。
在Windows程序中,消息是通过窗口句柄来进行分发和处理的。每个窗口都有一个唯一的窗口句柄(HWND),它用于标识和引用该窗口。通过窗口句柄,可以将收到的消息正确地分发给相应的窗口。
当Windows操作系统产生一个消息并将其放入消息队列时,消息中会包含目标窗口的句柄。然后,在应用程序的消息循环中,通过处理该消息的程序会根据消息中的窗口句柄,将消息分发给对应的窗口进行处理。
在处理消息的过程中,应用程序可以使用窗口句柄来识别消息应该属于哪个窗口。通常,在处理消息的代码中,可以使用如下方式来判断消息属于哪个窗口:
●检查消息中的窗口句柄与已创建的窗口句柄是否匹配。这可以通过保存窗口句柄的变量或数据结构来完成,然后将收到的消息中的窗口句柄与保存的窗口句柄进行比较。
●使用GetWindowLongPtr函数或GetWindowLong函数获取窗口的扩展信息(例如窗口的额外数据)。可以根据扩展信息中的标识或其他自定义数据来判断消息是属于哪个窗口。
●使用其它窗口属性或特征来判断。(例如窗口类名、窗口标题等)。
问题似乎回到了原点,最初消息被Windows操作系统捕获时就应该确定其属于哪个窗口。我们分类来讨论。
1.如果是鼠标消息,Windows系统会根据鼠标消息的坐标位置来确定它所处的位置属于哪个窗口。当然也有另外一种情况,就是一个窗口会主动捕获并拥有一个鼠标消息。
2.如果是键盘消息,那么该键盘消息一定是属于当前处于焦点状态的窗口,即当前拥有键盘输入焦点的窗口。如果要想改变键盘消息所属的窗口,只能是切换焦点窗口。
3.如果是子窗口控件、菜单、快捷键消息,其本身就属于指定的窗口,毋庸置疑。
■窗口消息队列
窗口消息队列是Windows操作系统中负责存储消息的一个缓冲区。每个线程都有自己的消息队列,用于接收和存储操作系统发送给应用程序的消息。在Windows程序中,消息队列起到了重要的作用,用于传递各种事件和请求,例如键盘输入、鼠标点击、窗口尺寸改变等。
消息队列是一个先进先出(FIFO)的数据结构,新的消息会被添加到队列的尾部,而从队列头部会取出最早进入队列的消息。当操作系统接收到一个消息时,会将该消息放入相应线程的消息队列中,然后通知该线程有新消息到达。
2.3.2 消息循环
应用程序可以通过消息循环来检查和处理消息队列中的消息。在消息循环中,应用程序会不断地从消息队列中取出消息进行处理,直到队列为空或者收到退出消息。
一般来说,消息循环具有以下形式:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
在这个消息循环中,GetMessage函数会从消息队列中取出一条消息,并将其存储在 msg 变量中。
■键盘消息
如果是键盘按键消息,TranslateMessage函数会将虚拟键消息翻译为字符消息。它会检查消息的类型,如果是某些特殊键(如功能键、方向键等),则生成一个 WM_KEYDOWN 或 WM_KEYUP 消息;如果是一个字符键消息,则生成一个 WM_CHAR 消息。TranslateMessage 的作用是为了在键盘输入时生成合适的字符消息。接着将WM_CHAR 消息重新送入消息队列。
■其他消息
其他消息则直接由DispatchMessage 函数将消息分发到相应的窗口过程函数进行处理。它会根据消息中的窗口句柄找到对应的窗口过程函数,并将消息传递给它。窗口过程函数负责处理具体的消息,它会根据消息类型和需要执行相应的逻辑,比如绘图、更新窗口状态、响应用户输入等。
总结一下,窗口消息队列是用于存储消息的缓冲区,当操作系统有消息需要传递给应用程序时,会将消息放入消息队列中。应用程序在消息循环中检查消息队列,从中取出消息并将其发送给相应的窗口过程函数进行处理。这样,应用程序能够接收并响应用户输入和操作系统事件。
在Windows程序中,如果是由我们主动发送消息呢?则需要调用下面两个函数。
●PostMessage函数:
PostMessage是一个Windows API函数,用于将一个消息放入与指定窗口关联的线程的消息队列中,等待线程处理。此消息并不会直接发送到窗口过程函数,而是立即返回,消息的处理可能会在稍后才进行。
以下是PostMessage函数的原型:
BOOL PostMessage(
HWND hWnd, // 窗口句柄
UINT Msg, // 消息
WPARAM wParam, // 额外的消息相关信息
LPARAM lParam // 额外的消息相关信息
);
参数含义:
hWnd:接收消息的窗口句柄。这可以是具体的窗口句柄,也可以是一些特殊值,如HWND_BROADCAST(指所有窗口)。如果此参数为NULL,则消息会发送给调用线程的消息队列。
Msg:需要发送的消息码,如WM_CLOSE。
wParam,lParam:与消息有关的额外信息。
PostMessage函数将消息投递到消息队列后就会立即返回,不管消息是否被目标窗口过程函数处理。也就是说PostMessage是异步的,主要用于在不要求立即处理消息的情况下通知其他窗口一个事件发生,比如通知其他窗口更新显示内容。
如果你希望立即发送并处理消息,应该使用SendMessage函数。不同于PostMessage,SendMessage会立即触发窗口的窗口过程函数处理消息,并等待处理完毕后返回。
●SendMessage函数:
SendMessage是一个Windows API函数,用于将一个消息发送到指定的窗口,并等待接收消息的窗口处理完该消息后返回。这个函数与PostMessage的最大区别就是:SendMessage是同步的,消息会立即被处理,而不是被投递到消息队列中等待处理。
以下是SendMessage函数的原型:
LRESULT SendMessage(
HWND hWnd, // 窗口句柄
UINT Msg, // 消息
WPARAM wParam, // 额外的消息相关信息
LPARAM lParam // 额外的消息相关信息
);
参数含义:
hWnd:接收消息的窗口句柄。这可以是具体的窗口句柄,也可以是一些特殊值。
Msg:需要发送的消息码,如WM_CLOSE。
wParam,lParam:与消息有关的额外信息。
SendMessage函数会立即调用目标窗口的窗口过程,传递给它消息码和额外信息,等待窗口过程处理完消息后再返回。这意味着在消息处理完成前,SendMessage函数会一直被阻塞。
请注意,对于可能会导致长时间等待的操作(如网络操作或大量计算),不应使用SendMessage进行处理,因为这可能造成应用程序的阻塞。此时,应使用PostMessage进行异步操作,或者设计一种将这种操作异步化的机制。
■窗口程序运行过程
如图所示,我们把Windows程序分为4个部分:Windows操作系统、主程序、窗口过程、其他应用程序。
●主程序我们已经非常熟悉了,分为五个固定的步骤,主程序的核心是一个消息循环,调用GetMessage函数不间断的从窗口消息队列获取消息。
●窗口过程负责处理消息,switch结构不处理的消息都交给DefWindProc默认窗口过程处理,处理后返回Windows系统。
●Windows操作存在一个总的消息队列和各个不同进程的窗口消息队列。Windows系统负责捕获消息或产生消息。如果是鼠标键盘消息则送入消息队列,非队列消息直接传递给窗口过程处理。Windows系统中的USER32.dll负责处理窗口界面处理。
●其他应用程序也可能向本进程发送消息。如果调用PostMessage函数向指定窗口过程发送消息,该消息将被分发到本进程的窗口消息队列。如果调用的是SendMessage函数发送的消息,则该消息会被直接发送给指定的窗口过程处理。
举例
一个完整的消息传递过程:
第一步:Windows操作系统捕获键盘消息WM_KEYDOWN,并将该消息送入操作系统总消息队列。
第二步:Windows系统将键盘消息分发给当前焦点窗口的窗口消息队列。
第三步:主程序的消息循环GetMessage函数从消息队列中获取键盘消息。
第四步:TranslateMessage函数将WM_KEYDOWN消息转换为WM_CHAR消息,并将WM_CHAR消息重新送入消息队列。
第五步:DispatchMessage函数将WM_CHAR消息分发给Windows系统。
第六步:Windows系统将WM_CHAR消息传递给窗口过程WndProc函数处理。
第七步:WndProc函数的switch结构中处理WM_KEYDOWN消息(MessageBox窗口显示按键消息的虚拟键码),处理完之后将控制器交还给Windows操作系统。
第八步:Windows系统将任务切换到主程序,消息循环GetMessage函数继续从消息队列中获取消息,如果消息队列中没有消息,则继续等待。
请读者写一个实例程序,测试上述消息循环过程。请单步跟踪程序执行的过程。
相关推荐
- go语言也可以做gui,go-fltk让你做出c++级别的桌面应用
-
大家都知道go语言生态并没有什么好的gui开发框架,“能用”的一个手就能数的清,好用的就更是少之又少。今天为大家推荐一个go的gui库go-fltk。它是通过cgo调用了c++的fltk库,性能非常高...
- 旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗
-
这几天老毛桃整理了几个微型Linux发行版,准备分享给大家。要知道可供我们日常使用的Linux发行版有很多,但其中的一些发行版经常会被大家忽视。其实这些微型Linux发行版是一种非常强大的创新:在一台...
- codeblocks和VS2019下的fltk使用中文
-
在fltk中用中文有点问题。英文是这样。中文就成这个样子了。我查了查资料,说用UTF-8编码就行了。edit->Fileencoding->UTF-8然后保存文件。看下下边的编码指示确...
- FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库
-
FLTK(FastLightToolkit)是一个轻量级的跨平台GUI库,特别适用于开发需要快速、高效且简单界面的应用程序。本文将介绍Python中的FLTK库,包括其特性、应用场景以及如何通过代...
- 中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux
-
IT之家1月29日消息,去年6月份,中科院大学教授、中科院计算所研究员包云岗,发布了开源高性能RISC-V处理器核心——香山。近日,包云岗在社交平台晒出图片,香山芯片已流片,回片后...
- Linux 5.13内核有望合并对苹果M1处理器支持的初步代码
-
预计Linux5.13将初步支持苹果SiliconM1处理器,不过完整的支持工作可能还需要几年时间才能完全完成。虽然Linux已经可以在苹果SiliconM1上运行,但这需要通过一系列的补丁才能...
- Ubuntu系统下COM口测试教程(ubuntu port)
-
1、在待测试的板上下载minicom,下载minicom有两种方法:方法一:在Ubuntu软件中心里面搜索下载方法二:按“Ctrl+Alt+T”打开终端,打开终端后输入“sudosu”回车;在下...
- 湖北嵌入式软件工程师培训怎么选,让自己脱颖而出
-
很多年轻人毕业即失业、面试总是不如意、薪酬不满意、在家躺平。“就业难”该如何应对,参加培训是否能改变自己的职业走向,在湖北,有哪些嵌入式软件工程师培训怎么选值得推荐?粤嵌科技在嵌入式培训领域有十几年经...
- 新阁上位机开发---10年工程师的Modbus总结
-
前言我算了一下,今年是我跟Modbus相识的第10年,从最开始的简单应用到协议了解,从协议开发到协议讲解,这个陪伴了10年的协议,它一直没变,变的只是我对它的理解和认识。我一直认为Modbus协议的存...
- 创建你的第一个可运行的嵌入式Linux系统-5
-
@ZHangZMo在MicrochipBuildroot中配置QT5选择Graphic配置文件增加QT5的配置修改根文件系统支持QT5修改output/target/etc/profile配置文件...
- 如何在Linux下给zigbee CC2530实现上位机
-
0、前言网友提问如下:粉丝提问项目框架汇总下这个网友的问题,其实就是实现一个网关程序,内容分为几块:下位机,通过串口与上位机相连;下位机要能够接收上位机下发的命令,并解析这些命令;下位机能够根据这些命...
- Python实现串口助手 - 03串口功能实现
-
串口调试助手是最核心的当然是串口数据收发与显示的功能,pzh-py-com借助的是pySerial库实现串口收发功能,今天痞子衡为大家介绍pySerial是如何在pzh-py-com发挥功能的。一、...
- 为什么选择UART(串口)作为调试接口,而不是I2C、SPI等其他接口
-
UART(通用异步收发传输器)通常被选作调试接口有以下几个原因:简单性:协议简单:UART的协议非常简单,只需设置波特率、数据位、停止位和校验位就可以进行通信。相比之下,I2C和SPI需要处理更多的通...
- 同一个类,不同代码,Qt 串口类QSerialPort 与各种外设通讯处理
-
串口通讯在各种外设通讯中是常见接口,因为各种嵌入式CPU中串口标配,工业控制中如果不够还通过各种串口芯片进行扩展。比如spi接口的W25Q128FV.对于软件而言,因为驱动接口固定,软件也相对好写,因...
- 嵌入式linux为什么可以通过PC上的串口去执行命令?
-
1、uboot(负责初始化基本硬bai件,如串口,网卡,usb口等,然du后引导系统zhi运行)2、linux系统(真正的操作系统)3、你的应用程序(基于操作系统的软件应用)当你开发板上电时,u...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- go语言也可以做gui,go-fltk让你做出c++级别的桌面应用
- 旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗
- codeblocks和VS2019下的fltk使用中文
- FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库
- 中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux
- Linux 5.13内核有望合并对苹果M1处理器支持的初步代码
- Ubuntu系统下COM口测试教程(ubuntu port)
- 湖北嵌入式软件工程师培训怎么选,让自己脱颖而出
- 新阁上位机开发---10年工程师的Modbus总结
- 创建你的第一个可运行的嵌入式Linux系统-5
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)