当前位置: 首页 > 编程日记 > 正文

Windows 消息循环(1) - 概览

本文从消息循环是如何驱动程序的这个角度,对 Windows 消息循环进行概览性介绍。

使用 EN5 课件获得更好的阅读体验:
【希沃白板5】课件分享 : 《Windows培训 - 消息循环》
https://r302.cc/q2d1jB
点击链接直接预览课件

1 程序是怎么跑起来的?

class Program
{static void Main(string[] args){Console.WriteLine("Hello Cvte.");Console.ReadLine();}
}

这是一段 C# Main 函数,如果不写 Console.ReadLine(); ,则程序会“一闪而过”,写了 Console.ReadLine(); 程序会阻塞,可以查看结果。
下面看一段复杂一点点的:

Console.WriteLine("Starting, Input Something:");
while (true)
{string input = Console.ReadLine();if (input == "exit"){break;}else{Console.WriteLine(!string.IsNullOrWhiteSpace(input)? $"Your Input to lower is:{input.ToLower()}": "You Inputted Nothing");}
}

这里有一个 while 循环,这样程序就可以一直运行了,我们可以说:这个程序由这个 while 循环驱动。

那,Windows 程序是由什么驱动的,答案呼之欲出:“消息循环”。

662394-20190223164148234-2114172478.png

2 消息循环的数据结构

typedef struct {  HWND hwnd;      // 消息的目标窗口句柄UINT message;   // 消息标识WPARAM wParam;  // 16位的参数LPARAM lParam;  // 32位的参数DWORD time;     // 消息发生的时间POINT pt;       // 消息发生时,鼠标的屏幕坐标
} MSG, *PMSG; 

2.1 消息的分类

消息的取值范围是 0x0000 - 0xFFFF。

从 0x0000 到 0x03FF,为系统定义的消息,常见的 WM_PAINT、WM_CREATE 等均在其中;
从 0x0400 到 0x7FFF,专用于用户自定义的消息,可以使用 WM_USER + x 的形式自行定义,其中WM_USER 的值就是 0x0400,x 取一个整数;
从 0x8000 到 0xBFFF,从 Windows 95 开始,也用作用户自定义的消息范围,可以使用 WM_APP + x 的形式自行定义。
根据微软的建议,WM_APP类消息用于程序之间的消息通信,而 WM_USER 类消息则最好用于某个特定的窗口类。
微软自己遵循这一惯例,所以,公用控件的消息,如 TVM_DELETEITEM,基本都是 WM_USER 类属。
从 0xC000 到 0xFFFF,这个区段的消息值保留给 RegisterWindowMessage 这个 API,此 API 可以接受一个字符串,把它变换成一个唯一的消息值。

3 消息的处理流程

消息产生 => 消息队列 => 消息循环 => 消息处理

3.1 消息产生

消息产生的源头

  • 系统
    一部分由输入设备(键盘鼠标等)产生,如 WM_MOUSEMOVE 。
    一部分由系统User库自己产生,User部分(或者是系统内的其他部分通过User部分)为了实现自身的正常行为或者管理功能而主动生成的。如 WM_WINDOWPOSCHANGED。

  • 应用程序自定义的消息

消息产生的方式

这里说主要的两个消息产生函数

  • SendMessage
    等待消息处理完成后,SendMessage才返回。
    深入一点的表达式:等待窗口处理函数返回后,SendMessage才返回。

  • PostMessage
    不等待消息处理完成,立刻返回。
    PostMessage只管发送消息,消息有没有被送到则并不关心,只要发送了消息,便立刻返回。

两个问题:
问:消息产生之后到了哪里?
答:消息队列。

问:SendMessage 产生的消息,会进入消息队列吗?
答:在同一个线程内,SendMessage 会直接调用目标窗口的窗口过程函数处理消息,并等待其返回。
跨线程的情况,SendMessage 会将消息发送到目标线程的消息队列(高优先级,排序在前)。然后等待目标线程的返回值。

3.2 消息队列

  • 系统消息队列
    接收输入设备的消息,分配给线程消息队列。
    输入设备(键盘、鼠标或者其他)的驱动程序会把用户的操作输入转化成消息放置于系统队列中,然后系统会把此消息转到目标窗口所在线程的消息队列中等待处理。

  • 线程(UI)消息队列
    当前UI线程中的消息。
    每一个GUI线程都会维护这样一个线程消息队列。(这个队列只有在线程调用 User 或者 GDI 函数时才会创建,默认并不创建)。然后线程消息队列中的消息会被本线程的消息循环(有时也被称为消息泵)派送到相应的窗口过程(也叫窗口回调函数)处理。

两个问题:
问:消息队列属于谁?
答:属于UI线程(不属于窗口)。

问:非UI线程有消息队列吗?
没有。

3.3 消息循环

while(GetMessage(&msg, NULL,0, 0))  
{  TranslateMessage(&msg);  DispatchMessage (&msg);  
}   

如上,消息循环就是一个 while 循环,与文章最开始提到 while 向呼应。
其中 GetMessage 取出消息,TranslateMessage 翻译消息,DispatchMessage 调度消息。

问:消息循环属于谁?
答:每一个UI线程有一个消息循环(不是每一个窗口。)

消息循环的另一个样子:

while (!done)
{if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){if (msg.message == WM_QUIT){done = TRUE;}else{TranslateMessage(&msg);DispatchMessage(&msg);}}else{// 还可以驱动点别的事情,如 openGL 绘图。}
}

分别来看:

  • 取出消息

GetMessage
GetMessage会阻塞等待,直到取到一个消息。

PeekMessage
PeekMessage则不阻塞,立即返回。
PeekMessage有一个标志参数,这个标志参数指定了如果队列中如果有消息的话,PeekMessage 的行为。
如果该标志中含有 PM_REMOVE,则 PeekMessage 会把新消息返回到 MSG 结构中,正如 GetMessage 的行为那样。
如果标志中指定了 PM_NOREMOVE,则不会从消息队列中移除任何消息。

  • 翻译消息
    望文生义地看,翻译消息是对消息数据结构进行某种转换吗?
    不是的,TranslateMessage不修改原有消息,只在特定情况下产生新的消息。

TranslateMessage函数不修改由参数lpMsg指向的消息结构。
仅为那些由键盘驱动器映射为ASCII字符的键产生WM_CHAR消息。
如:
消息WM_KEYDOWN和WM_KEYUP组合产生一个WM_CHAR或WM_DEADCHAR消息。
消息WM_SYSKEYDOWN和WM_SYSKEYUP组合产生一个WM_SYSCHAR或 WM_SYSDEADCHAR 消息。

所以,如果程序中没有字符处理的需要,这句是可以不要的。

  • 分派消息
    将消息分配给 hwnd 指定的窗口函数,让其处理。
    如果没有找到对应的窗口,则丢弃。

3.4 消息处理

消息在消息循环中被分配到指定的窗口过程函数,由其处理。

// 有删减的窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{switch (message){case WM_COMMAND:case WM_PAINT:case WM_CREATE:default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}

回顾两个问题:
问:WndProc 函数由谁调用?
答:DispatchMessage or SendMessage。
从上文中可以看到,窗口过程函数不是有程序员自己调用的,而是系统在恰当的时机调用,这个时机就是 DispatchMessage or SendMessage。

问:未处理的消息交给谁?
答:DefWindowProc。
DefWindowProc只处理关闭等感兴趣的消息,其它的消息则忽略。

回顾

消息队列和消息循环属于UI线程,窗口没有,其它普通线程没有。
窗口有自己的窗口过程函数,消息在这里被处理。
消息循环驱动整个程序跑起来。

想一睹消息循环究竟是如何跑起来的?
原始 win32 窗口是如何被创建的?

在 VS 中,新建一个win32的窗口程序,即可看到。

662394-20190223164240846-562137478.png

转载于:https://www.cnblogs.com/jasongrass/p/10423196.html

相关文章:

得到的旋转向量和平移向量转换成旋转矩阵 (SE(3))

理论过程 头文件说明 1.使用罗德里格斯公式需要包含头文件为#include<opencv2/calib3d.hpp> 2.使用函数cv2eigen需要包含头文件<opencv2/core/eigen.hpp>&#xff0c;但是在则之前要包含一个对eigen定义的头文件&#xff0c;否则会报eigen.hpp文件的错&#xff0c;…

[译稿]同步复制提议 2010-09

2019独角兽企业重金招聘Python工程师标准>>> 同步复制9/2010议案 目录 [隐藏] 1 PAGE状态 2 有何不同之处这个补丁&#xff1f; 3 同步复制概述 4 用户视角 4.1 管理员视角 5 实现 5.1 STANDBY 5.2 MASTER 6 CODE 7 性能分析 8 尚未实现 9 &#xff0c;其他问题 PAG…

STL vector

1、vector是表示可变大小数组的序列容器。 2、就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;而且它的大小会…

JavaScript 复习之数据类型

一、简介 JavaScript 的数据类型有 7 种&#xff1a; 数值&#xff08;number&#xff09;字符串&#xff08;string&#xff09;布尔值&#xff08;boolean&#xff09;undefinednull对象&#xff08;object&#xff09;Symbol&#xff08;ES6 中新增的类型&#xff0c;表示独…

Nginx反代配置

一、ngx_http_proxy_module模块ngx_http_proxy_module模块可根据用户请求的uri传递至后端服务器&#xff0c;实现反向代理命令&#xff1a;1.proxy_pass设置一个代理服务器的地址&#xff0c;协议&#xff0c;和一个可选的URI的位置应该映射。作为一个协议&#xff0c;“HTTP”…

STL queue priority_queue

queue 队列 queue 队列 就像排队一样&#xff0c;后来的排在后面&#xff0c;先来的先出队。所以只能对队首和队尾的元素进行操作 back()返回最后一个元素empty()如果队列空则返回真front()返回第一个元素pop()删除第一个元素push()在末尾加入一个元素size()返回队列中元素的…

镜头评价指标及测试方法(二)—畸变与分辨率

一、畸变 畸变&#xff08;distortion&#xff09;也称为失真&#xff0c;是由于光阑球差的影响&#xff0c;不同视场的主光线通过光学系统后与高斯像面的交点高度不等于理想像高&#xff0c;两者之差就是畸变。因此畸变只改变轴外物点在理想面上的成像位置&#xff0c;使像的形…

优秀Java程序员应该知道的20个实用开源库

一个优秀且经验丰富的Java开发人员的特点之一是对API的广泛了解&#xff0c;包括JDK和第三方库。我花了很多时间学习API&#xff0c;特别是在阅读Effective Java 3rd Edition之后&#xff0c;Joshua Bloch建议如何使用现有的API进行开发&#xff0c;而不是为常用的东西写新的代…

sersync 同步

Sersync数据同步rsync是类unix系统下的数据镜像备份工具——remote sync。一款快速增量备份工具 Remote Sync&#xff0c;远程同步支持本地复制&#xff0c;或者与其他SSH、rsync主机同步。它的特性如下&#xff1a;v 可以镜像保存整个目录树和文件系统。v 可以很容易做到保持原…

python-opencv 形态学

五、形态学运算 检测边和 角点 形态学算子检测图像中的边缘和拐角&#xff08;实际用&#xff1a;Canny或Harris等算法&#xff09; 5.1 检测边缘 形态学检测边缘的原理&#xff1a;在膨胀时&#xff0c;图像中的物体会想周围“扩张”&#xff1b;腐蚀时&#xff0c;图像中的物…

【 HihoCoder】1082 The Marshtomp has seen it all before (暴力 或 脑力)

https://vjudge.net/problem/HihoCoder-1082 暴力 #include <iostream> #include <cstring>using namespace std;char s[205];int main() {int i;while(gets(s)){i 0;int len strlen(s);while(i<len){if((s[i]m||s[i]M)&&(s[i1]a||s[i1]A)&&am…

java 多维数组转化为字符串

int[][] a {{1,2,3},{4,5,7}};System.out.println(Arrays.deepToString(a)); Arrays.deepToString()此方法是为了将多维数组转换为字符串而设计的。 三维二维都可以解决;转载于:https://www.cnblogs.com/lishuaiqi/p/10428612.html

python-opencv 轮廓检测

轮廓检测 图像处理中经常用到轮廓检测,OpenCV-python接口中使用cv2.findContours()函数来查找检测物体的轮廓。 import cv2img cv2.imread(D:\\test\\contour.jpg) gray cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, binary cv2.threshold(gray,127,255,cv2.THRESH_BINARY)…

JavaDate类

在JDK1.0中&#xff0c;Date类是唯一的一个代表时间的类&#xff0c;但是由于Date类不便于实现国际化&#xff0c;所以从JDK1.1版本开始&#xff0c;推荐使用Calendar类进行时间和日期处理。这里简单介绍一下Date类的使用。1、使用Date类代表当前系统时间Date d new Date();Sy…

Jupyter Notebook使用教程

1、启动 在终端中输入以下命令&#xff1a; jupyter notebook --allow-root执行命令之后&#xff0c;在终端中将会显示一系列notebook的服务器信息&#xff0c;同时浏览器将会自动启动Jupyter Notebook。 启动过程中终端显示内容如下&#xff1a; $ jupyter notebook [I 08…

一次性理清JavaScript变量等高难度面试问题

主要是解决JavaScript中比较难懂的部分&#xff0c;当然了&#xff0c;这部分经常在面试题中露面&#xff0c;这篇文章主要是讲解解题思路&#xff0c;对新手会有很大帮助&#xff08;如果你仔细看的话&#xff09;。书籍方面&#xff0c;我看的是《你不知道的javascript》,精髓…

yolov3(一:模型训练)

第一部分&#xff1a;训练已有的voc datasets 搞清楚该算法的模型训练流程 Darknet是Joseph维护的开源的神经网络框架&#xff0c;使用C语言编写&#xff1a;https://pjreddie.com/darknet/yolo/ Darknet快速&#xff0c;易于安装&#xff0c;同时支持CPU和GPU计算&#xff1…

Ios生产证书申请(含推送证书)

一、Mac机上生成请求文件。 Mac机上点击证书助手 > 从证书颁发机构请求证书 > 得到CertificateSigningRequest.certSigningRequest请求文件&#xff08;此请求证书建议一直保存&#xff0c;因为根证书的生成需要使用此请求文件&#xff0c;根证书多个app可以使用一个&…

【Python】SyntaxError: unexpected EOF while parsing

找到错误的地方是 少打了半个括号 emmm 1、可能是语法问题&#xff0c;需要自己检查代码 2、可能是用python2.7来运行python3.0的代码不兼容

华为云家庭视频监控帮你一起守护家

设想一下&#xff1f;离开家了突然想起家里空调没关怎么办&#xff1f;家里没人有陌生人入侵了怎么办&#xff1f;不在家家里老人出事了怎么办&#xff1f; 不用愁&#xff0c;总有办法解决的&#xff0c;这些日常生活中极容易遇到的事情&#xff0c;没有分身术的时候&#xff…

SLAM之特征匹配(三)————RANSAC------LO-RANSAC Algorithm

matlab 编译loransac&#xff0c;lapack mex ranH.c时一直链接错误。 原来mex在编译多个文件时要把所有的C文件都列出来。命令如下&#xff1a; mex loransacH.mex.c ranH.c utools.c Htools.c lapwrap.c matutl.c rtools.c -ID:\lapack\headers\lapack -LD:\lapack - lcbia.l…

【Codeforces】1136C Nastya Is Transposing Matrices (矩阵转置)

http://codeforces.com/contest/1136/problem/C 第一个矩阵可否通过转置&#xff0c;变换成第二个矩阵&#xff0c;可以的话输出“YES”&#xff0c;不可以的话&#xff0c;输出“NO” 转置之后&#xff0c;对角线元素是不变的 用map&#xff0c;或者vector 都可以 #includ…

linux基础篇-02,linux时间管理date hwclock cal 简述

################################################时间管理1&#xff0c;date:系统时钟查看当前系统时间[rootJameszhan etc]# date2016年 11月 14日 星期一 20:16:37 CST################################################设定系统时间 2016年 07月 20日 星期三 10:30:00 CST…

Spark shuffle调优

Spark shuffle是什么Shuffle在Spark中即是把父RDD中的KV对按照Key重新分区&#xff0c;从而得到一个新的RDD。也就是说原本同属于父RDD同一个分区的数据需要进入到子RDD的不同的分区。现在的spark版本默认使用的是sortshuffle;shuffle在哪里产生shuffle在spark的算子中产生,也就…

多传感器融合之滤波(一)——卡尔曼滤波(KF)推导

c参考资料&#xff1a;https://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/ 卡尔曼滤波本质上是一个数据融合算法&#xff0c;将具有同样测量目的、来自不同传感器、(可能) 具有不同单位 (unit) 的数据融合在一起&#xff0c;得到一个更精确的目的测量值。 卡尔…

【HDU】1284 钱币兑换问题 (想一想)

http://acm.hdu.edu.cn/showproblem.php?pid1284 除以三&#xff0c;看最多能放多少个三分的硬币&#xff0c;加一表示全部都是一分的硬币着一种情况。之后用一个循环看一下&#xff0c;有多少种情况 因为确定了有几个三分&#xff0c;有几个两分&#xff0c;一分的也就自然…

抓取网页的脚本 【修复】

之前张耀老师的网页脚本由于51cto升级&#xff0c;课程列表页面改用javascript失效了笔者发现视频课程页面右边的列表都为静态化后的视频课程地址&#xff0c;遂将老师初始版本的脚本进行了修改&#xff0c;在对视频课程页面使用时结果正常&#xff0c;遂将及修改后的脚本和部分…

实例规格 ECS (共享计算型)和 (通用型-原独享)性能上有什么区别?...

实例规格 ECS (共享计算型)和 (通用型-原独享)性能上有什么区别? 实例规格 共享计算型 和 通用型(原独享), 如果同样是2核4G 或者4核8G ; 性能上有什么差异/差距大吗? 内存型比通用性性能好些&#xff0c;而且CPU和内存配比&#xff1a; 通用型为1&#xff1a;2&…

yolov3(二:车牌识别)

0.按照&#xff1a;https://blog.csdn.net/Darlingqiang/article/details/103889245步骤训练自己的模型 1.下载[data.zip]&#xff1a;链接: https://pan.baidu.com/s/1NahLmB5YajUJT_Gk1OgN7A 提取码: 8888 2.进入data/voc目录下运行voc_label.bat重新生成2019_train.txt, 201…

[转]CSS hack大全详解

转自&#xff1a;CSS hack大全&详解 1、什么是CSS hack?CSS hack是通过在CSS样式中加入一些特殊的符号&#xff0c;让不同的浏览器识别不同的符号&#xff08;什么样的浏览器识别什么样的符号是有标准的&#xff0c;CSS hack就是让你记住这个标准&#xff09;&#xff0c;…