窗口机制 消息循环 消息队列

  • 格式:doc
  • 大小:182.00 KB
  • 文档页数:9

注:以下所说windows线程是指与调用了与图形用户界面相关的函数的线程,而windows 进程则是包含windows线程的进程,以区别于一般的进程和线程一个线程只有一个报文队列,但可以有多个窗口,每个窗口都有WndProc函数窗口对象如下:Typedef struct _WINDOW_OBJECT{PW32THREADINFO ti;PDESKTOP Desktop;……UNICODE_STRING WindowName;……RECT WindowRect;……//pointer to the owning thread’s message queuePUSER_MESSAGE_QUEUE MessageQueue; //指向所属进程的消息队列Struct _WINDOW_OBJECT *FirstChild;Struct _WINDOW_OBJECT *LastChild;Struct _WINDOW_OBJECT *NextSibling;Struct _WINDOW_OBJECT *PrevSibling;//ENTRY in the list of thread windowsLIST_ENTRY ThreadListEntry;//Handle to the owner windowHWND hOwner;……WNDPROC WndProc; //消息处理函数PRTHREAD OwnerThread; //指向具体窗口所属线程的ETHREAD结构……}WINDOW_OBJECT;当一个线程第一次被建立时,系统假定线程不会被用于任何与用户相关的任务。

这样可以减少线程对系统资源的要求。

但是,一旦这个线程调用一个与图形用户界面有关的函数(例如检查它的消息队列或建立一个窗口),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。

因此进程在建立之初,并没有与用户界面相关的数据结构,只有当进程中某一线程调用win32k.sys中的函数时,进程会转化为windows进程,调用为win2k.sys的线程则转化为windows线程。

一个进程只要其中有一个线程是windows线程,则就是windows进程。

Windows进程除了EPROCESS和KPROCESS外,还有个W32PROCESS结构,即系统为windows进程所分配的另外一些资源。

且EPROCESS结构中的指针Win32Process指向这个数据结构。

当线程转化为windows线程时,系统会为线程分配一个T H R E A D I N F O结构这个T H R E A D I N F O结构包含一组成员变量,利用这组成员,线程可以认为它是在自己独占的环境中运行。

T H R E A D I N F O是一个内部的、未公开的数据结构,用来指定线程的投递消息队列(posted-message queue)、发送消息队列(send-message queue)、应答消息队列(r e p l y -message queue)、虚拟输入队列(virtualized-input queue)、唤醒标志(wake flag)、以及用来描述线程局部输入状态的若干变量。

这里的登记消息即为投递消息(posted-message)注意:这里的各个消息队列的消息均指的时被投递的消息,被发送的消息……即发送方直接把消息放入接收方的相应消息队列当线程有了与之相联系的T H R E A D I N F O结构时,线程就有了自己的消息队列集合。

Windows中有两种类型的消息队列1) 系统消息队列(System Message Queue)这是一个系统唯一的Queue,设备驱动(mouse, keyboard)会把操作输入转化成消息存在系统队列中,然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理2) 线程消息队列(Thread-specific Message Queue)每一个GUI线程都会维护这样一个线程消息队列。

(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。

然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理. typedef struct tagMSG {HWND hwnd; //消息要发向的窗口句柄UINT message; //消息标示符,以WM_开头的预定义值WPARAM wParam;LPARAM lParam;DWORD time; //消息放入消息队列的时间POINT pt; //消息放入消息队列时鼠标位置} MSG,*PMSG;附:第二个成员变量message指定了消息的标识符。

在Windows中,消息是由一个数值来表示的,不同的消息对应不同的数值。

但是由于数值不便于记忆,所以Windows将消息对应的数值定义为WM_XXX宏(WM是Window Message的缩写)的形式,XXX对应某种消息的英文拼写的大写形式。

消息可分为两类:队列消息和非队列消息队列消息:消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理,包括:投递消息(posted-message)输入消息或虚拟输入消息(input-message or virtualized-input message)非队列消息:消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理,包括:发送消息(sent-message)一、将消息投递到线程的投递消息队列BOOL PostMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);PostMessage函数为每个被投递的消息创造一个MSG结构,并且把消息复制给相应的消息队列当一个线程调用这个函数时,系统要确定是哪一个线程建立了用h w n d参数标识的窗口。

然后系统分配一块内存,将这个消息参数存储在这块内存中,并将这块内存增加到相应线程的投递消息队列中。

并且,这个函数还设置Q S _ P O S T M E S S A G E唤醒位。

函数P o s t M e s s a g e在登记了消息之后立即返回,调用该函数的线程不知道登记的消息是否被指定窗口的窗口过程所处理。

二、向窗口发送消息LRESULT SendMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);使用S e n d M e s s a g e函数直接将窗口消息直接发送给一个窗口过程,窗口过程将处理这个消息。

只有当消息被处理之后,S e n d M e s s a g e才能返回到调用程序。

A、向线程自己的某个窗口发送消息LRESULT DeliverMessage(HWND hwnd,UNIT uMsg,WPARAM wparam,LPARAM lparam){ W NDPROC lpfnWndProc = (WNDPROC)GetWindowLongPtr(hwnd,GW;P_WNDPROC);Return CallWindowProc(lpfnWndProc,hwnd,uMsg,wParam,lParam);}//一个线程向线程自己的某个窗口发送消息,接收窗口的过程直接被调用LRESULT SendMessage_SameThread(HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam){return DeliverMessage(hwnd, uMsg, wParam, lParam);}B、向其他线程的窗口发送消息首先,发送的消息要追加到接收线程的发送消息队列,同时还为这个线程设定Q S _ S E N D M E S S A G E标志。

其次,如果接收线程已经在执行代码并且没有等待消息(如调用Ge t M e s s a g e、P e e k M e s s a g e或Wa i t M e s s a g e),发送的消息不会被处理,系统不能中断线程来立即处理消息。

当接收进程在等待消息时,系统首先检查Q S _ S E N D M E S S A G E唤醒标志是否被设定,如果是,系统扫描发送消息队列中消息的列表,并找到第一个发送的消息。

Void DeliverInComingSentMessage(){While(有一个发送消息){MSG msg = 这个消息;从发送消息队列删除这个消息;if(这是一个特殊的消息){处理这个特殊的消息;}else{DeliverMessage(msg.hwnd,msg.message,msg.wParam,msg.lParam);把返回值返回给发送者;}}}附:函数中所说的“特殊的消息”是指采用S e n d M e s s a g e C a l l b a c k函数时,函数会立即返回,而当消息真正被处理后,接收线程会向发送端的应答队列发送一个消息,此时的消息即为“特殊的消息”SendToAnotherThread(...){将消息添加到接受者的发送消息队列中;//这两个函数将等待接收者响应if (Function == SendMessage || Function == SendMessageTimeout) { while (! message reply received && ! timed out) {DeliverlncomingSentMessages();等待新的消息或者 (如果是 SendMessageTimeout)超时;}}}当一个线程等待S e n d M e s s a g e返回时,它基本上是处于空闲状态。

但它可以执行一个任务:如果系统中另外一个线程向一个窗口发送消息,这个窗口是由这个等待S e n d M e s s a g e返回的线程所建立的,则系统要立即处理发送的消息。

在这种情况下,系统不必等待线程去调用G e t M e s s a g e、Peek Message或Wa i t M e s s a g e。

因为SendMessage函数只有等到消息被处理了才能返回,所以有可能会引起线程挂起,可以采用S e n d M e s s a g e Ti m e o u t、S e n d M e s s a g e C a l l b a c k、S e n d N o t i f y M e s s a g e和R e p l y M e s s a g e这四个函数防止这种情况出现三、消息的处理在消息的接收端,消息处理的原则:1、发送消息将被直接递送到窗口过程中2、接收投递消息(通过GetMessage或PeekMessage)接收输入消息(通过GetMessage或PeekMessage)一个线程必须将消息队列中的消息移除并进行处理,这包括两方面的内容:●消息循环●窗口过程—接收并处理发送给窗口的消息消息循环:MSG msg;BOOL bRet;while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0){if (bRet == -1){// handle the error and possibly exit}else{TranslateMessage(&msg); //键盘输入翻译为可传递的信息,即windows内部定义的信息,WM_xxxDispatchMessage(&msg); //派发消息,最终是对具体应用程序WndProc函数的调用,每个视窗都有个WndProc函数}}1、GetMessage/PeekMessage:GetMessage与PeekMessage的主要区别在于GetMessage只有在获得了一个投递消息或输入消息之后才会返回,而PeekMessage发完消息后,不管目标是否响应,都会从函数调用中返回,也还有其他一些次要的区别BOOL PeekMessage(LPMSG pmsg, HWND hwnd, UINT wMsgFilterMin,UINT wMsgFilterMax, UINT flags){DeliverlncomingSentMessages(); //前面有其伪代码if (有某个投递消息满足过滤条件) {*pmsg = 这个消息;if (flags & PM__REMOVE) 从投递消息队列中删除这个消息;return TRUE;}// 注意 WM_QUIT 消息将忽略过滤器if (有一个 WM_QUIT 消息正在等待处理并且没有投递消息) {清除 "WM_QUIT消息正在等待处理"的标志;*pmsg = WM_QUIT消息;return TRUE;}if (鼠标移动并且输入消息满足过滤条件) {清除"鼠标已移动"的标志;将WMJVIOUSEMOVE消息添加到输入消息队列中;}i f (有某个输入消息满足过滤条件) {*pmsg = 这个消息;if (flags & PM_REMOVE) 从输入消息队列删除这个消息;r e t u r n TRUE;}if (窗口需要进行绘制并且WM_PAINT消息满足过滤条件) {*pmsg = WM_PAINT消息;return TRUE;}if (计时器被触发了,并且WM_TIMER消息满足过滤条件) {将一个WM_TIMER消息添加到投递消息队列;*pmsg = 这个消息;if (flags & PM_REMOVE) 从投递消息队列中删除这个消息;return TRUE;}return FALSE;}// GetMessage只有在获得了一个投递消息或输入消息之后才会返回BOOL GetMessage(LPMSG pmsg, HWND hwnd, UINT wMsgFilterMin, UINT wMsgFilterMax){while (!PeekMessage(pmsg, hwnd,wMsgFilterMin, wMsgFilterMax, PM_REMOVE)) {WaitMessage ();// wait for a new message to arrive}return pmsg->message != WM_QUIT;}GetMessage过程示意如下:我们只能将投递消息和输入消息过滤,不能过滤掉发送消息,这些消息是肯定会被处理的WM_PAINT、WM_TIMER、WM_QUIT消息比较特别,系统总是将消息投递到消息队列的最后,这是为了确保窗口其他信息能够在先进先出的消息队列中被处理。