真正理解微软Windows程序运行机制——什么是消息

这篇具有很好参考价值的文章主要介绍了真正理解微软Windows程序运行机制——什么是消息。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

windos的消息机制,Visual Studio技术,microsoft,windows,学习方法,visual studio,c++,Powered by 金山文档

我是荔园微风,作为一名在IT界整整25年的老兵,今天说说Windows程序的运行机制。经常被问到MFC到底是一个什么技术,为了解释这个我之前还写过帖子,但是很多人还是不理解。其实这没什么,我在学生时代也被这个问题困绕过。而且那个时间学习资料没有那么丰富,网上也没有什么资料,周围也没有懂的人,那个时候理解MFC更困难。甚至在我看来,理解这个比理解人工神经网络更难。

我认为造成这种现象的根本原因就是没有搞清楚Windows程序的运行机制,因为不理解Windows程序的运行机制,所以给理解MFC带来了很大的困难。我决定带所有微软开发技术的初学者一起攻破这个问题,但是一篇文章肯定是讲不清楚的,我们要分好几章来说。需要你有足够的耐心,一起来吧。我们这次来搞清楚什么是Windows程序的消息。

消息?难道是你路上遇到一个熟人然后朝熟人喊一声就叫消息?这个概念让很多人产生严重误解。

要了解什么是消息,要先了解什么是事件。事件可分为几种,一是由输入设备触发的,比如鼠标键盘等等。二是由窗体控件触发的,比如Button控件,File菜单等。最后是由来自Windows内部的事件。这三种称为事件,而事件产生消息。

消息,就是指Windows发出的一个通知,告诉应用程序某个事件发生了。例如你单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。消息本身是作为一个记录传递给应用程序的,这个记录(一般在 C语言中称为“结构体”)中包含了消息的类型以及其他信息。例如,对单击鼠标所产生的消息来说,这个结构体中包含了单击鼠标的消息号(WM_LBUTTONDOWN)、单击鼠标时的坐标(由X,Y值连接而成的一个32位整数)。这个结构体类型叫做MSG。

在 Windows程序中,消息是由MSG结构体来表示的。MSG结构体的定义如下:

typedef struct tagMSG{
  HWND hwnd;
  UINT message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD time;
  POINT pt;
}MSG;

typedef struct POINT
{
   int x;
   int y;
}POINT;

该结构体中各成员变量的含义如下:

第一个成员变量hwnd表示消息所属的窗口。在windows上通常开发的程序都是窗口应用程序,一个消息一般都是与某个窗口相关联的。例如,在某个活动窗口中按下鼠标左键,产生的按键消息就是发给该窗口的。在Windows程序中,用HWND类型的变量来标识窗口。说到消息就不能不说窗口句柄,系统通过窗口句柄来在整个系统中唯一标识一个窗口,发送一个消息时必须指定一个窗口句柄表明该消息由那个窗口接收。而每个窗口都会有自己的窗口过程,所以用户的输入就会被正确的处理。例如有两个窗口共用一个窗口过程代码,你在窗口1上按下鼠标时消息就会通过窗口1的句柄被发送到窗口1而不是窗口2。如果对HWND不明白,马上看我的另一篇文章:

真正理解微软Windows程序运行机制——什么是句柄

第二个成员变量 message指定了消息的标识符。在Windows中,消息是由一个数值来表示的,不同的消息对应不同的数值。但是由于数值不便于记忆,所以Windows将消息对应的数值定义为“WM_名称”的宏的形式,其中的名称对应某种消息的英文拼写的大写形式。例如,鼠标左键按下消息是WM_LBUTTONDOWN,键盘按下消息是WM_KEYDOWN,字符消息是WM_CHAR等。在程序中我们通常都是以这种宏的形式来使用消息的。如果想知道“WM_名称”消息对应的具体数值,可以在 Visual Studio 2022的代码编辑窗口中选中“WM_名称”,然后单击鼠标右键,在弹出菜单中选择“转到定义”,即可看到该宏的具体定义。跟踪或查看某个变量的定义,都可以使用这个方法。

第三个和第四个成员变量wParam和IParam,这两个变量我当年学了很久很久才搞清楚是什么意思。其实这两个变量就是用于指定消息的附加信息。例如,当我们收到一个字符消息的时候,message成员变量的值就是WM_CHAR,但用户到底输入的是什么字符,那么就由wParam和IParam来说明。wParam、IParam表示的信息随消息的不同而不同。如果想知道这两个成员变量具体表示的信息,可以在MSDN中关于某个具体消息的说明文档查看到。读者可以在Visual Studio 2022的开发环境中通过“转到定义”查看一下WPARAM和 LPARAM这两种类型的定义,可以发现这两种类型实际上就是unsigned int和 long。

第五个变量time表示消息投递到消息队列中的时间。第六个变量pt表示消息投递到消息队列中鼠标的当前位置,可以看看上面的代码,并不复杂。

好,我们总结一下,消息的组成情况:一个消息由一个消息名称(UINT),和两个参数(WPARAM,LPARAM)。当用户进行了输入或是窗口的状态发生改变时系统都会发送消息到某一个窗口。例如当菜单选中之后会有WM_COMMAND消息发送,WPARAM的高字中(HIWORD(wParam))是命令的ID号,对菜单来讲就是菜单ID。当然用户也可以定义自己的消息名称,也可以利用自定义消息来发送通知和传送数据。

windos的消息机制,Visual Studio技术,microsoft,windows,学习方法,visual studio,c++,Powered by 金山文档

在上世纪末本世纪初,我还在用C99写程序的时候,当调用函数打开文件,这个库函数最终调用操作系统提供的函数来打开文件。而在Windows中,用户程序可以调用系统的API函数,系统也会调用用户程序,这个调用是通过消息来进行的。与基于DOS的应用程序不同,Windows的应用程序是事件消息驱动的。Windows的程序不会显式地调用函数来获取输入,而是等待windows操作系统向它们传递输入。所以说,Windows程序设计是一种完全不同于传统的DOS方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要是基于消息的。

也就是说每个在windows操作系统上的应用程序都会在启动后(比如被你双击桌面图标)开始接收操作系统抛过来的各种各样的数值,或者叫各种各样的参数(比如你点了应用程序窗口上的什么按钮)。然后你的应用程序根据这些参数来进行相应的操作和运行。

例如,当用户在窗口中输入文本的时候,按下鼠标左键,此时,操作系统会感知到这一事件,于是将这个事件包装成一个消息,放入到应用程序的消息队列中,就好像大家在排队一样,如下图。然后应用程序从消息队列中取出消息并进行响应,也就是响应这个鼠标的操作。在这个处理过程中,操作系统也会给应用程序“发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。

windos的消息机制,Visual Studio技术,microsoft,windows,学习方法,visual studio,c++,Powered by 金山文档

每一个Windows应用程序在开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。例如,当我们按下鼠标左键的时候,将会产生WM_LBUTTONDOWN消息,系统会将这个消息放到窗口所属的应用程序的消息队列中,等待应用程序的处理。Windows将产生的消息依次放到消息队列中,而应用程序则通过一个消息循环不断地从消息队列中取出消息,并进行响应。这种消息机制就是Windows程序运行的机制。

这里我们思考一下,谁将收到消息??一个消息必须由一个窗口接收。在窗口的过程(WNDPROC)中可以对消息进行分析,对自己感兴趣的消息进行处理。例如你希望对菜单选择进行处理那么你可以定义对WM_COMMAND进行处理的代码,如果希望在窗口中进行图形输出就必须对WM_PAINT进行处理。谁来处理这个事,程序员来负责,不然窗口怎么响应这些消息?

Windows系统把应用程序的输入事件传递给各个窗口,每个窗口有一个函数,称为窗口消息处理函数。窗口消息处理函数处理各种用户输入,处理完成后再将控制权交还给系统。窗口消息处理函数一般是在注册一个窗口的时候指定的。你可以从典型的SDK程序中窗口消息处理函数是怎么声明和实现的。

Windows通过消息的形式向窗口传递用户输入。消息可以由系统和应用程序生成。该系统会为每个输入事件产生相应的消息,用户点击鼠标、移动鼠标或滚动条,或是应用程序改变了系统的某些属性,比如说系统更改了字体资源,改变了某个窗口的大小。应用程序可以生成消息,通告发送消息指定它的窗体去执行某些任务或者是与其他的应用程序交互。windows系统将消息发送到一个窗口消息处理函数时传递四个参数:窗口句柄,消息标识符,两个DWORD值(消息参数)。窗口句柄标识了该消息的目的窗口。Windows使用它来确定是哪个窗口的的窗口消息处理函数收到该消息。

一个消息标识符是一个有名字的常量,用来表明消息的意义。当一个窗口处理函数收到一条消息,它根据判断消息标识符来决定如何处理该消息,例如,消息标识符WM_PAINT消息告诉窗口程序窗口的客户区已发生变化,必须重绘。 消息参数(DWORD值)指定传递的数据或是数据的地址。消息参数可以是一个整型值,一个指针值。也可以为NULL。一个窗口过程必须根据消息标识符来确定如何解释消息参数。

windows 消息类型主要分为系统定义的消息和应用程序定义的消息。产生的消息首先由Windows系统捕获,放在系统消息队列,再拷贝到对应的应用程序消息队列。32或64位系统为每一个应用程序维护一个消息队列。系统为每个应用程序维护一个消息循环,消息循环会不断检索自身的消息队列。每有一个消息,就用GetMessage()取出消息。

while(GetMessage (&msg, NULL, 0, 0))//Windows消息循环。
 
{
    TranslateMessage (&msg) ;//翻译消息,如按键消息,翻译为WM_CHAR

    DispatchMessage (&msg) ;//分发消息到对应窗口

}

GetMessage具有阻塞机制。当消息队列中没有消息时,程序非忙等,而是让权等待。当收到WM_QUIT时,GetMessage返回false,循环停止,同时应用程序终止。在消息处理中:DispatchMessage()把取出来的消息分配给相应的窗口或线程,由窗口过程处理函数DefWindowProc()处理。

Windows的应用程序靠消息驱动来实现功能。而消息驱动靠消息机制来处理。消息机制就是由消息队列,消息循环,消息处理构成的。那么,消息机制是如何运作的呢?

当用户运行一个应用程序,通过对鼠标的点击或键盘按键,产生一些特定事件。由于Windows一直监控着I/O设备,该事件首先会被翻译成消息,由系统捕获,存放于系统消息队列。经分析,Windows知道该消息应由那个应用程序处理,则拷贝到相应的应用程序消息队列。由于消息循环不断检索自身的消息队列,当发现应用程序消息队列里有消息,就用GetMessage()取出消息,封装成Msg()结构。如果该消息是由键盘按键产生的,用TranslateMessage()翻译为WM_CHAR消息,否则,用DisPatchMessage()将取出的消息分发到相应的应用程序窗口,交由窗口处理程序处理。Windows为每个窗体预留了过程窗口函数,该函数是一个回掉函数,由系统调用,应用程序不能调用。程序员可以通过重载该函数处理我们”感兴趣”的消息。对于不感兴趣的消息,则由系统默认的窗口过程处理程序做出处理。一定仔细看下图,这个图是无意中在百度图片里看到的,不知道作者是谁。这幅图是我认为画的最好的一幅:

windos的消息机制,Visual Studio技术,microsoft,windows,学习方法,visual studio,c++,Powered by 金山文档

消息机制的整个完整过程就是:当运行程序->事件操作引发消息->消息先存在系统消息队列->再存入到应用程序消息队列->用消息循环提取消息->处理消息->再返回消息队列。

同样,我们可以通过自定义用户消息,用SendMessage()函数向窗口模拟发送消息,再重载窗口过程处理函数DefWndProc()来接收用户自定义消息,并作出处理。因为SendMessage()是系统的一个API函数。

int a1 = 0x340;
int a2 = 0x350; //用户自定义消息。

[DllImport("User32.dll",EntryPoint="SendMessage")]
//EntryPoint 表示要调用的函数入口点是dll文件里的SendMessage()函数。

private static extern int SendMessage(
  ntPtr hwnd,   //IntPtr是平台特定的int类型。就是在32跟64位系统上分别为32位和64位
  int Msg,    //表明要发送的消息。
   int wParam,  //32位的附加信息。
   int IPrarm  //32位的附加信息。(随消息的改变而改变)
);

// 此处由于SendMessage发送到本线程的窗口。所以发送的消息不会被加入到消息队列中,
//所以通过PeekMessage()或GetMessage()不能获取到由SendMessage发送的消息。
1 protected override void DefWndProc(ref Message m)//重载接收处理函数
2 {
3     switch (m.Msg)
4   {
5     case a1: label1.Text = "做第一个动作"; break;
6     case a2: label1.Text = "做第二个动作"; break; 
7     default: base.DefWndProc(ref m); break;
8   }
9 }

总结

我们把上面这些过程再总结一下,以窗体的创建为例。Windows应用程序实际上具有相同的程序结构和执行控制流程。执行流程如下:程序入口点(WinMain())->注册窗口类(RegisterClass)->创建窗口(CreateWindow())->显示窗口(ShowWindow(hwnd,nCmdShow)->(UpdateWindow(hwnd))->消息循环(等待用户操作窗口产生消息)->放入消息队列->消息循环往复读取并执行相应代码->窗口函数(决定在窗口那里显示些什么,或者如何响应用户输入(如上面的SendMessage()函数))->消息处理(用来确定窗口函数接收的是什么消息以及如何处理,如DefWndProc()函数。

系统定义的消息是指操作系统向应用程序发送消息来和应用程序通讯。操作系统通过消息控制应用程序的运行,向应用程序传递用户输入以及一些其他有用的信息。应用程序也可以发送系统定义的消息,应用程序通过这些消息去控制使用注册窗口类创建的控件的窗口的运行。每个系统定义的消息都有一个唯一的消息标识符和相应的符号常量。符号常量通常会表明系统定义的消息所属的类别。不同的前缀表明不同的类别。应用程序定义的消息是指应用程序可以通过创建自定义的消息,用来和自己的窗口和其他进程通讯。如果应用程序创建了自己的消息,窗口处理函数可以解析这些信息并作出相应的处理。

Windows程序中的消息可以分为“进队消息”和“不进队消息”,分出两种队,如下图。进队的消息将由系统放入应用程序的消息队列中,由应用程序取出并发送。不进队的消息在系统调用窗口过程时直接发送给窗口。不管是进队消息还是不进队消息,最终都由系统调用窗口过程函数对消息进行处理。也就是说,windows使用两种方法将消息派发到一个窗口消息处理函数:一是将消息放到消息队列(先进先出队列),二是不放到消息队列,直接发送到窗口消息处理函数,让窗口处理函数来处理消息。派发到消息队列的消息被称为排队消息。它们主要是用户输入事件,比如说鼠标或键盘消息盘,有WM_MOUSEMOVE消息,WM_LBUTTONDOWN,WM_KEYDOWN,和WM_CHAR消息。还有一些其他的,包括WM_TIMER,WM_PAINT,以及WM_QUIT。大多数其他的消息息,这是直接发送到窗口过程,被称为非队列消息(non queued messages)。

windos的消息机制,Visual Studio技术,microsoft,windows,学习方法,visual studio,c++,Powered by 金山文档

再思考一下,未处理的消息到那里去了?操作系统为窗口编写了默认的窗口过程,这个窗口过程将负责处理那些你不处理消息。正因为有了这个默认窗口过程我们才可以利用Windows的窗口进行开发而不必过多关注窗口各种消息的处理。例如窗口在被拖动时会有很多消息发送,而我们都可以让系统自己去处理。

最后我们用一段代码演示如何在窗口过程中处理消息

LONG yourWndProc(HWND hWnd,UINT uMessageType,WPARAM wP,LPARAM)
{
         switch(uMessageType)
               {
                 //使用SWITCH语句将各种消息分开
                 case(WM_PAINT):
                      doWindow(...);//在窗口需要重新绘制时进行输出
                 break;
                 case(WM_LBUTTONDOWN):
                      doWork(...);//在鼠标左键被按下时进行处理
                 break;
                 default:
                      callDefaultWndProc(...);//对于其它情况就让系统自己处理
                 break;
         }
}

好,总结一下,消息机制就是系统维护了一个或多个消息队列,所有产生的消息都回被放入或是插入队列中。系统会在队列中取出每一条消息,根据消息的接收句柄而将该消息发送给拥有该窗口的程序的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统所以Windows可以同时进行多个任务。

各位小伙伴,这次我们就说到这里,下次我们再深入研究windows程序运行机制。

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。文章来源地址https://www.toymoban.com/news/detail-768606.html

到了这里,关于真正理解微软Windows程序运行机制——什么是消息的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 怎么才能真正理解服务器是什么?

    构成互联网世界的基本节点是一个又一个的计算机和网络设备。 服务器是提供特定服务的计算机,你平时用的计算机叫做终端设备。他们在机器上的本质是一样的,但因为承担不同的角色,所以有一些区别: 0、归属与成本 服务器:属于提供服务者,一般是公司等团体组织需

    2024年02月09日
    浏览(41)
  • 微软MFC技术运行机制

    我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下微软MFC技术运行机制。 很多初学者误以为VC++开发必须使用MFC,其实不一定的。MFC的使用只能是提高程序在某些情况下的开发效率,而不能替代整个Win32程序设计。我认为我们有必要再来好好讲讲MFC的本质、MFC中的

    2024年02月08日
    浏览(43)
  • windows窗口创建流程及window消息机制详解

    本文通过对windows窗口创建的流程来分析在windows系统中消息的产生、获取、处理的方法,详细介绍windows的消息机制,方便Windows开发者对windows的消息机制进行理解。 定义:窗口类是一个属性集,是Windows编程中用于创建窗口的模板。窗口类包含了窗口的各种信息的数据结构,每

    2024年02月05日
    浏览(78)
  • 深入理解Windows操作系统机制(二)

    我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows这个我们熟悉的不能再熟悉的系统。 我们每天都在用Windows操作系统,但是其实我们每天直接在打交道的并不是Windows操作系统的内核,而是Windows操作系统的人机交互界面,这个界面其实只是Window

    2024年02月17日
    浏览(55)
  • “深入探索JVM内部机制:理解Java虚拟机的运行原理“

    标题:深入探索JVM内部机制:理解Java虚拟机的运行原理 摘要:本篇博客将深入探索Java虚拟机(JVM)的内部机制,帮助读者理解JVM的运行原理。我们将介绍JVM的组成结构,包括类加载器、运行时数据区域和执行引擎,并通过示例代码解释这些概念的具体应用。 正文: 一、J

    2024年02月11日
    浏览(47)
  • 微软MFC程序运行的正确顺序

     我是荔园微风,作为一名在IT界整整25年的老兵,今天来看一下微软MFC程序运行的正确顺序。这真的只是一个小众话题。但是对理解MFC很重要。 很多人写了一堆MFC程序,却不知道MFC程序是怎么运行顺序的。我们就来看一看这个问题。 我在之前的帖子中多次提到,main()函数也

    2024年02月13日
    浏览(34)
  • Android HAL机制的深入理解及在Linux上移植和运行的一个好玩的HAL小例子

    PS:要转载请注明出处,本人版权所有。 PS: 这个只是基于《我自己》的理解, 如果和你的原则及想法相冲突,请谅解,勿喷。 环境说明   Ubuntu 18.04.x   近一年来,虽然还是做的是AIOT相关的事情,但是某些事情却发生了一些变化。随着个人的阅历提升,现在的AI在边缘端

    2023年04月08日
    浏览(46)
  • 4.小程序的运行机制

    把小程序的代码包下载到本地 解析app.json全局配置文件 执行app.js小程序入口文件,调用App()创建小程序的实例 渲染小程序首页 小程序启动完成 加载解析页面的.json配置文件 加载页面.wxml模板和.scss样式 执行页面的.ts文件,调用Page()创建页面实例 页面渲染完成

    2024年02月12日
    浏览(34)
  • 真正理解红黑树,真正的(Linux内核里大量用到的数据结构

    作为一种数据结构,红黑树可谓不算朴素,因为各种宣传让它过于神秘,网上搜罗了一大堆的关于红黑树的文章,不外乎千篇一律,介绍概念,分析性能,贴上代码,然后给上罪恶的一句话,它最坏情况怎么怎么地... 我们想,一棵二叉树怎么就是最坏情况,那就是它退化为一

    2024年02月16日
    浏览(38)
  • 微信小程序的【运行机制】解读

    前面我们有章节给大家讲到了,微信小程序的生命周期钩子函数,那么大家知道,这些不同的生命周期函数,是在什么样的操作运行模式下触发的吗?本章节就给大家整理了有关于 微信小程序 的操作运行机制,以便于对后面微信小程序的开发理解。 微信小程序从启动到最终

    2024年02月06日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包