Windows窗口消息
不看不知道,一看吓一跳。原来就只是单单理解了SendMessage和PostMessage。前者是发送完要处理后再返回,后者是发送后立即返回,不管有没有处理。但今天仔细看了书上讲解后,才发现原来事情多着呢。
窗口对象:线程里会有两种特别的对象,即窗口对象和挂钩对象。要知道,进程是分配资源的单位,因此,如果我们创建了某些资源,当我们没有明确要求释放的时候,这些资源只有在进程退出时才被释放。 但窗口对象和挂钩对象不同,他们是属于创建他们的线程的。原因是,窗口和挂钩对象的消除需要依靠WM_DESROY和WM_NCDESTROY消息,而如果线程退出了,消息循环不再继续,窗口收不到销毁信息,自然是释放不掉了。因此,如果一个线程创建了一个窗口或是安装了一个挂钩,线程结束后,操作系统会自动删除窗口或卸载挂钩。
首先来看看,消息的拥有者,消息队列。
THREADINFO:
每个创建窗口的线程,系统都会为之分配一个THREADINFO结构的实例。这个结构如下
Struct THREADINFO
{
PostMessageQueue; 登记消息队列
VirtualInputMessageQueue; 虚拟输入队列
SendMessageQueue; 发送消息队列
ResultMessageQueue; 应答消息队列
nExitCode; 退出码
bActive; 唤醒标志
LocalInputStateValue; 局部输入状态变量
}
当线程有了THREADINFO后,这个线程就有了自己的消息队列集合。这是每个线程专有的,有多少个线程创建了窗口,就有多少个这样的集合。
PostMessage:
先看看UBOOL PostMessage( HWND hwnd,UINT msg,WPARAM,LPARAM);
当一个线程调用这个函数时,系统要确定是哪一个线程建立了用hwnd参数标识的窗口。然后系统分配一块内存,将这个消息参数存在这块内存中。并将这块内存增加到相应线程的登记消息队列中(Post)。并且,这个函数还设置QS_POSTMESSAGE唤醒位,这个位.然后就返回.调用这个函数的线程并不知道这个消息有没有被处理.
也可以通过UBOOL PostThreadMessage( DWORD dwThreadId,UINT msg,WPARAM,LPARAM);函数将消息直接放置在线程的消息队列中.通常选择HWND和THREADID中较方便的一个. 这个函数和上一个一样,发出消息后立即返回.
向线程发送消息的还有VOID PostQuitMessage(int nExitCode);也可以调用PostThreadMessage(GetCurrentThreadId(),NULL,WM_QUIT,nExitCode,0);来完成这个功能.值得注意的是, PostQuitMessage并不会将消息放入线程的消息队列.只是在内部PostQuitMessage设置QS_QUIT唤醒标志.并设置THREADINFO结构的nExitCode成员.
SendMessage
下面来看看SendMessage函数.它与PostMessage有着同样的参数.但是,当一个线程发送一个消息的时候,窗口过程将处理这个消息.只有当消息被处理后.SendMessage函数才返回.
SendMessage发送消息给调用线程和发送消息到另外一个线程的情况是不一样的. 当发送给自己的时候,SendMessage做了类似如下的工作.
SendMessage(NULL,nMsg,wParam,lParam)
{
Return MsgProc(NULL,nMsg,wParam,lParam);
}
可以看出,SendMessage只是简单地调用了自己的窗口处理函数,然后待处理完成之后,便返回.但是,当一个线程向其它线程发送消息的时候,情况就复杂多了. 为了保证这个SendMessage函数是要在被处理后再返回,那么就需要线程间的同步.下面将展现了将要做的事.
1 系统将发送的消息追加到接受线程的消息队列.同时为这个线程设置QS_SENDMESSAGE标志
2 如果接收线程并没有在等待消息(即不是处于GetMessage,PeekMessage,WaitMessage)的时候,发送的消息不会处理.但是,系统不能中断线程来处理这个消息. 如果接收线程是在等待消息.那么系统首先检测QS_SENDMESSAGE标志,如果是,系统便扫瞄消息列表,并找到第一个消息. 注意,系统只会依次处理他消息队列中的消息. 这个来自于其它线程的消息并不会得到优待.
3 当接收线程在处理消息的时候,发送线程将挂起.并被设置为空闲状态,这表示他在等待他的应答队列中出现消息.当接收线程处理完该消息后.会向发送线程的应答消息队列中发送一个消息,以告知消息处理完成. 当一个线程在等待消息时,它几乎是处于空闲状态.但是它可以执行一个任务.就是如果系统中另外一个线程向这个发送线程发送消息的时候.则它要立即处理这个来自于其它线程的消息.在这种情况下,系统不必等待线程去调用GetMessage,PeekMessage,WaitMessage);
SendNotifyMessage
还有一个SendNotifyMessage函数.这个函数发送一个消息后便立即返回. 与PostMessage不同的是,它发送的消息是加入到发送消息队列中的. 而由于系统处理发送消息队列的优先级比登记(post)消息要高. 并且,当一个线程调用这个函数来向自己发送消息时,它的作用和SendMessage一样.