新书推荐:2.3 消息机制

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进行异步操作,或者设计一种将这种操作异步化的机制。

       ■窗口程序运行过程

       如图2-10所示,我们把Windows程序分为4个部分:Windows操作系统、主程序、窗口过程、其他应用程序。

       ●主程序我们已经非常熟悉了,分为五个固定的步骤,主程序的核心是一个消息循环,调用GetMessage函数不间断的从窗口消息队列获取消息。

       ●窗口过程负责处理消息,switch结构不处理的消息都交给DefWindProc默认窗口过程处理,处理后返回Windows系统。

       ●Windows操作存在一个总的消息队列和各个不同进程的窗口消息队列。Windows系统负责捕获消息或产生消息。如果是鼠标键盘消息则送入消息队列,非队列消息直接传递给窗口过程处理。Windows系统中的USER32.dll负责处理窗口界面处理。

       ●其他应用程序也可能向本进程发送消息。如果调用PostMessage函数向指定窗口过程发送消息,该消息将被分发到本进程的窗口消息队列。如果调用的是SendMessage函数发送的消息,则该消息会被直接发送给指定的窗口过程处理。

             

图2-10  窗口程序运行过程

举例

       一个完整的消息传递过程:

       第一步: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函数继续从消息队列中获取消息,如果消息队列中没有消息,则继续等待。

请读者写一个实例程序,测试上述消息循环过程。请单步跟踪程序执行的过程。

本文摘自编程达人系列教材《Windows API每日一练》。公众号滴水编程达人。

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

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

相关文章

用 Axios 封装一个双 token 无感刷新

为什么要用双Token无感刷新,它解决了什么问题? 为了保证安全性,后端设置的Token不可能长期有效,过了一段时间Token就会失效。而发送网络请求的过程又是需要携带Token的,一旦Token失效,用户就要重新登陆&…

欢乐打地鼠小游戏html源码

这是一款简单的js欢乐打地鼠游戏,挺好玩的,老鼠出来用鼠标点击锤它,击中老鼠获得一积分。 欢乐打地鼠小游戏html源码

kopf,一个实用的 Python 库!

更多Python学习内容:ipengtao.com 大家好,今天为大家分享一个实用的 Python 库 - kopf。 Github地址:https://github.com/nolar/kopf 在 Kubernetes 中,Operator 是一种用于扩展 Kubernetes 功能的强大工具。Operator 可以自动化应…

MySQL的group by与count(), *字段使用问题

文章目录 问题group by到底做了什么举个例子简单来说为什么select字段,count()不能和*共同使用总结 问题 这是一段摘抄自MySQL官网的文字。其大致意思是MySQL拓展了group by的使用,MySQL允许选择没有出现在group by中的字段。换句话说,标准SQ…

【QT5.14.2】编译MQTT库example的时候报No such file or directory

【QT5.14.2】编译MQTT库example的时候报No such file or directory 前几天导师让跑一下MQTT库,用的5.14.2版本的QT,于是就上网搜了一个教程:https://www.bilibili.com/video/BV1dH4y1e7hG/?spm_id_from333.337.search-card.all.click&v…

Fedora的远程桌面

要在 Fedora 40 上开启远程桌面功能。 首先,要确保已安装 gnome-remote-desktop 和 vino 包。 这些软件包通常默认安装在 Fedora 的 GNOME 桌面环境中。 可以按照以下步骤操作: 1、判断电脑是否安装了 gnome-remote-desktop 和 vino 包: tomfedora:…

第十三周 5.28 三个修饰符知识点

一、abstract[抽象的] 1.abstract可以修饰类: (1)被abstract修饰的类称为抽象类 (2) 语法:abstract class 类名{} (3) 特点:抽象类只能声明引用,不能创建对象 (4) 抽象类中可以定义属性和成员方法、构造方法 2.abstr…

【Linux】匿名管道的应用场景 --- 进程池

👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:Linux 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵,希望大佬指点一二 如果文章对…

Qt qtpropertybrowser使用实例(1)

属性界面实例&#xff1a; 代码如下&#xff1a; #include <QDate> #include <QLocale> #include "qtpropertymanager.h" #include "qtvariantproperty.h" #include "qttreepropertybrowser.h" int main(int argc, char *argv[]) {…

nginx mirror流量镜像详细介绍以及实战示例

nginx mirror流量镜像详细介绍以及实战示例 1.nginx mirror作用2.nginx安装3.修改配置3.1.nginx.conf3.2.conf.d目录下添加default.conf配置文件3.3.nginx配置注意事项3.3.nginx重启 4.测试 1.nginx mirror作用 为了便于排查问题&#xff0c;可能希望线上的请求能够同步到测试…

TalkingData 是一家专注于提供数据统计和分析解决方案的独立第三方数据智能服务平台

TalkingData 是一家专注于提供数据统计和分析解决方案的独立第三方数据智能服务平台。通过搜索结果&#xff0c;我们可以了解到 TalkingData 的一些关键特性和市场情况&#xff0c;并将其与同类型产品进行比较。 TalkingData 产品特性 数据统计与分析&#xff1a;提供专业的数…

【每日算法】

算法第15天| (二叉树part02)层序遍历、226.翻转二叉树(优先掌握递归)、101. 对称二叉树(优先掌握递归) 文章目录 算法第15天| (二叉树part02)层序遍历、226.翻转二叉树(优先掌握递归)、101. 对称二叉树(优先掌握递归)一、层序遍历二、226. 翻转二叉树(优先掌握递归)三、101. 对…

Elasticsearch index 设置 false,为什么还可以被检索到?

在 Elasticsearch 中&#xff0c;mapping 定义了索引中的字段类型及其处理方式。 近期有球友提问&#xff0c;为什么设置了 index: false 的字段仍能被检索。 本文将详细探讨这个问题&#xff0c;并引入列式存储的概念&#xff0c;帮助大家更好地理解 Elasticsearch 的存储和查…

基于STM32F030设计的多点温度采集系统(BC26+OneNet)

一、项目背景 随着物联网技术的迅猛发展&#xff0c;越来越多的智能设备应运而生&#xff0c;而温度采集系统是其中重要的一类。在现代工业和家庭生活中&#xff0c;温度对于生产、居住和储存等过程的控制有着非常重要的作用。因此&#xff0c;准确地采集环境温度数据并进行处…

HTML做成一个粒子漩涡特效页面

大家好&#xff0c;今天制作制作一个粒子漩涡特效的页面&#xff01; 先看具体效果&#xff1a; 要在一个单一的 index.html 页面中实现粒子漩涡特效&#xff0c;我们可以使用HTML、CSS和JavaScript&#xff08;不需要外部库&#xff09;。下面是一个简单的例子&#xff0c;展…

JWT 从入门到精通

什么是 JWT JSON Web Token&#xff08;JWT&#xff09;是目前最流行的跨域身份验证解决方案 JSON Web Token Introduction - jwt.ioLearn about JSON Web Tokens, what are they, how they work, when and why you should use them.https://jwt.io/introduction 一、常见会…

Git发布正式

一般我们开发都是在测试环境开发&#xff0c;开发完成后再发布到正式环境。 一.分支代码合并到主分支1.首先切换到自己的分支(比如分支叫&#xff1a;dev)git checkout dev2.把本地分支拉取下来git pull 或者 git pull origin dev3.切换到主分支mastergit checkout master4.更新…

【Vue】购物车案例-构建项目

脚手架新建项目 (注意&#xff1a;勾选vuex) 版本说明&#xff1a; vue2 vue-router3 vuex3 vue3 vue-router4 vuex4/pinia vue create vue-cart-demo需要勾选上vuex&#xff0c;由于这个项目只有一个页面&#xff0c;vuex可勾可不勾 将原本src内容清空&#xff0c;替换成教学…

【计算机网络基础】IP地址

文章目录 一、IP介绍IP地址和Mac地址IP地址分类 二、IPV4地址IPV4地址分类子网掩码进制转换方法8421法则转换法私网地址PNAT技术IP分配原则 三、IPv6地址IPV6组成IPV6分类IPV6特殊地址 四、VLSM可变长子网掩码划分子网VLSM优点 &#x1f308;你好呀&#xff01;我是 山顶风景独…