Windows消息队列、线程消息队列,窗口消息的概念与关系

1.窗口
Windows程序是由一系列的窗口构成的,每个窗口都有自己的窗口过程,窗口过程就是一个拥有有固定 Signature 的 C函数,具体格式如下:
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);


窗口类型:
可重叠窗口(Overlapped Window),
弹出窗口(Pop-up Window),
子窗口(Child Window)


窗口之间的关系: 父子关系,拥有关系,前后关系。


2.线程
一个进程至少拥有一个线程,称为主线程,如果一个线程创建了窗口,拥有GUI资源,那么也称该线程为GUI线程,否则就为工作线程。窗口是由线程创建的,
创建窗口的线程就拥有该窗口。这种线程拥有关系的概念对窗口有重要的意义:建立窗口的线程必须是为窗口处理所有消息的线程。为了使这个概念更加明
确具体,可以想像一个线程建立了一个窗口,然后就结束了。在这种情况下,窗口不会收到一个WM_DESTROY或WM_NCDESTROY消息,因为线程已经结束,不可能被用来使窗口接收和处理这些消息。每个线程,如果它至少建立了一个窗口,都由系统对它分配一个消息队列。这个队列用于窗口消息的派送(dispatch)。为了使窗口接收这些消息,线程必须有它自己的消息循环,消息循环一般如下:


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


应用程序不断的从消息队列中获取消息,然后系统通过DispatchMessage函数分派消息到相应窗口的窗口过程,使得消息得到处理。当获取到WM_QUIT消息时,GetMessage返回0,循环结束。


3.消息
消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息
给应用程序,它被定义为:
typedef struct {
HWND hwnd; //窗口句柄, 发生在哪个窗口上
UINT message; //消息标识号 ( WM_MOUSEMOVE, WM_LBUTTONDOWN, ... )
WPARAM wParam; //消息参数1
LPARAM lParam; //消息参数2
DWORD time;
POINT pt;
} MSG, *PMSG;
一个消息结构体包含了该事件 所有完备信息,当应用程序收到该消息时,就可以做出相应处理了。


消息分类:
<1>.队列消息和非队列消息
从消息的发送途径上看,消息分两种:队列消息和非队列消息。
队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
这里,对消息队列阐述如下:
Windows维护一个系统消息队列(System message queue),每个GUI线程有一个线程消息队列(Thread message queue)。鼠标、键盘事件由鼠标或键盘驱动程序转换成输入消息并把消息放进系统消息队列,例如WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN、WM_CHAR等等。Windows每次从系统消息队列移走一个消息,确定它是送给哪个窗口的和这个窗口是由哪个线程创建的,然后,把它放进窗口创建线程的线程消息队列。线程消息队列接收送给该线程所创建窗口的消息。线程从消息队列取出消息,通过Windows把它送给适当的窗口过程来处理。


除了键盘、鼠标消息以外,队列消息还有WM_PAINT、WM_TIMER和WM_QUIT。这些队列消息以外的绝大多数消息是非队列消息。


<2>.系统消息和应用程序消息
从消息的来源来看,可以分为:系统定义的消息和应用程序定义的消息。
系统消息ID的范围是从0到WM_USER-1,或0X80000到0XBFFFF;应用程序消息从WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范围的消息
由应用程序自己使用;0XC000到0XFFFF范围的消息用来和其他应用程序通信,为了ID的唯一性,使用::RegisterWindowMessage来得到该范围的消息ID。


<3>.窗口消息,命令消息,控件通知消息
根据处理过程的不同,可以分为三类:窗口消息,命令消息,控件通知消息。


(1).窗口消息
一般以WM_开头,如WM_CREATE, WM_SIZE, WM_MOUSEMOVE等标准的Windows消息, 用于窗口相关的事件通知,窗口消息将由系统分配到该窗口的窗口过程处理。
(2).命令消息 (WM_COMMAND)
一种特殊的窗口消息,它从一个窗口发送到另一个窗口以处理来自用户的请求,通常是从子窗口发送到父窗口,例如,点击按钮时,按钮的父窗口会收到
WM_COMMAND消息,用以通知父窗口按钮被点击,经测试:子窗口向父窗口发送WM_COMMAND消息,或者称为父窗口会收到WM_COMMAND消息,操作系统并不是通过将WM_COMMAND消息放入到父窗口的消息队列中去,而是直接调用了父窗口的窗口过程,以 WM_COMMAND 为消息标识参数(UINT uMsg),实现这个功能的API函数正是: LRESULT DispatchMessage(const MSG *lpmsg);
(3).控件通知消息
WM_NOTIFY消息,当用户与控件交互(Edit, Button...)时,通知消息会从控件窗口发送到父窗口,这种消息的目的不是为了处理用户命令,而是为了让父窗
口能够适时的改变控件。


windows 消息处理机制是这样的 :
首先 Windows OS 把来自硬件 ( 鼠标 , 键盘等消息 ) 和来自应用程序的消息放到一个 OS 系统消息队列中去 ,而应用程序需要有自己的消息队列 , 也就是线程消息队列,每一个线程有自己的消息队列,对于多线程的应用程序就有和线程数目相等的线程消息队列 .
windows 消息队列把得到的消息发送到线程消息队列 , 线程消息队列每次取出一条消息发送到指定窗口 , 不断循环直到程序退出 . 这个循环就是靠消息环
while(GetMessage() )
{
TranslateMessage();
DispatchMessage();
}
实现的 .GetMessage() 只是从线程消息中取出一条消息 , 而 DispatchMessage
则把取出的消息发送到目的窗口 . 如果收到 WM_CLOSE 消息则结束循环 , 发送 PostQuitMessage(0), 处理WM_DESTROY 销毁窗口
Windows 消息队列 UI 线程,窗口以及消息处理方式总结
1 Windows UI thread:
2 每个窗体是一个 UI thread ,还是只有一个 UI thread?答: 所有的窗体都用这一个 UI thread
3 Message queue: 每个窗体都有一个 message queue ,还是共用一个 message queue? 答:共用
4 message 处理:是同步还是异步。是每次处理一个消息等这个消息处理完后再处理另一个消息还是每次取一个不等这个消息处理完就处理下一个,也就是 dispatchmessage 什么时候返回?
答案是要等这个消息处理函数处理完以后才返回,否则会造成消息处理的混乱
5 message 的结构是什么:包含了窗体的 handle ,消息的类型等
在 Winuser.h 中有定义如下:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
其中的成员变量 message 是 WM_SIZE 、 WM_COMMAND 、 WM_QUIT 等等消息标识。
hwnd 中是这个消息所在的窗口句柄。
6 Windows 如何知道消息应该送到哪一个线程,
这里我们要分为两种情况 , 消息是不是队列消息 , 比如在一个窗体空白处点击左键 , 首先 OS 会根据当前的context 来生成 MSG , MSG 中会包括要发送到的窗口的 Handle, 这就是一个队列消息 , 首先 OS 会将这个消息放到 OS 的系统消息队列中 , 而后 OS 会有专门的进程根据 MSG 中的窗口的 Handle 找到创建该窗口的线程,而后将该 MSG 送到该线程的消息队列,而后由该消息循环来处理这个消息, 最终由DispatchMessage 函数来将这个消息送到相应的窗口处理函数。
如果你在一个窗体上点击了一个 button 呢,消息的路径是怎样的呢?当你点击了一个 button 后, OS 产生三个MSG。 WM_LBUTTONDOWN和WM_LBUTTONUP,这两个消息的窗口Handle为button的handle。一个WM_command 或者 wm_notify 消息, OS 会将这个消息直接送给包含 button 的 window processdure 来处理,而不会将这个送到消息队列。
相关文章
相关标签/搜索