windows消息机制

来源:互联网 发布:郭靖 知乎 编辑:程序博客网 时间:2024/06/10 14:52
程序建立的每一个窗口都有相关的窗口消息处理程序。


Windows程序开始执行后,Windows为该程序建立一个「消息队列」。这个消息队列用来存放该程序可能建立的各种不同窗口的消息。


程序中有一小段程序代码,叫做「消息循环」,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。


有些消息直接发送给窗口消息处理程序,不用放入消息队列中。


程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。


假设使用者执行HELLOWIN,并且使用者最终单击了 Close按钮,或者假设用键盘或鼠标从系统菜单中选择了 Close, DefWindowProc处理这一键盘或者鼠标输入,在检测到使用者选择了Close选项之后,它给窗口消息处理程序发送一条WM_SYSCOMMAND消息。WndProc将这个消息传给DefWindowProc。DefWindowProc给窗口消息处理程序发送一条WM_CLOSE消息来响应之。WndProc再次将它传给DefWindowProc。DestroyWindow呼叫DestroyWindow来响应这条WM_CLOSE消息。DestroyWindow导致Windows给窗口消息处理程序发送一条WM_DESTROY消息。WndProc再呼叫PostQuitMessage,将一条WM_QUIT消息放入消息队列中,以此来响应此消息。这个消息导致WinMain中的消息循环终止,然后程序结束。


DispatchMessage (&msg) ;        
又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。在HELLOWIN中,这个窗口消息处理程序就是WndProe函数。处理完消息之后,WndProc传回到Windows。此时,Windows还停留在DispatchMessage呼叫中。在结束DispatchMessage呼叫的处理之后,Windows回到HELLOWIN,并且接着从下一个GetMessage呼叫开始消息循环。


我们已经谈到过,Windows给窗口发送消息,这意味着Windows呼叫窗口消息处理程序。但是,Windows程序也有一个消息循环,它呼叫GetMessage从消息队列中取出消息,并且呼叫DispatchMessage将消息发送给窗口消息处理程序。


那么,Windows程序是依次等待消息(类似于普通程序中相同的键盘输入),然后将消息送到某地方去的吗?或者,它是直接从程序外面接收消息的吗?实际上,两种情况都存在。


消息能够被分为「队列化的」和「非队列化的」。队列化的消息是由Windows放入程序消息队列中的。在程序的消息循环中,重新传回并分配给窗口消息处理程序。非队列化的消息在Windows呼叫窗口时直接送给窗口消息处理程序。也就是说,队列化的消息被「发送」给消息队列,而非队列化的消息则「发送」给窗口消息处理程序。任何情况下,窗口消息处理程序都将获得窗口所有的消息--包括队列化的和非队列化的。窗口消息处理程序是窗口的「消息中心」。


队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出。队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。


非队列化消息则是其它消息。在许多情况下,非队列化消息来自呼叫特定的Windows函数。例如,当WinMain呼叫CreateWindow时,Windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息。当WinMain呼叫ShowWindow时,Windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息。当WinMain呼叫UpdateWindow时,Windows将给窗口消息处理程序发送WM_PAINT消息。键盘或鼠标输入时发出的队列化消息信号,也能在非队列化消息中出现。例如,用键盘或鼠标选择了一个菜单项时,键盘或鼠标消息就是队列化的,而说明菜单项已选中的WM_COMMAND消息则可能就是非队列化的。


这一过程显然很复杂,但幸运的是,其中的大部分是由Windows解决的,不关我们的程序的事。从窗口消息处理程序的角度来看,这些消息是以一种有序的、同步的方式进出的。窗口消息处理程序可以处理它们,也可以不处理。


当我说消息是以一种有序的同步的方式进出时,我是说首先消息与硬件的中断不同。在一个窗口消息处理程序中处理消息时,程序不会被其它消息突然中断。


虽然Windows程序可以多线程执行,但每个执行绪的消息队列只为窗口消息处理程序在该执行绪中执行的窗口处理消息。换句话说,消息循环和窗口消息处理程序不是并发执行的。当一个消息循环从其消息队列中接收一个消息,然后呼叫DispatchMessage将消息发送给窗口消息处理程序时,直到窗口消息处理程序将控制传回给Windows,DispatchMessage才能结束执行。


当然,窗口消息处理程序能呼叫给窗口消息处理程序发送另一个消息的函数。这时,窗口消息处理程序必须在函数呼叫传回之前完成对第二个消息的处理。那时窗口消息处理程序将处理最初的消息。例如,当窗口过程调用UpdateWindow时,Windows将呼叫窗口消息处理程序来处理WM_PAINT消息。窗口消息处理程序处理WM_PAINT消息结束以后,UpdateWindow呼叫将把控制传回给窗口消息处理程序。


这也就是说窗口消息处理程序必须是可重入。在大多数情况下,这不会带来问题,但是程序写作者应该意识到这一点。例如,假设您在窗口消息处理程序中处理一个消息时设置了一个静态变量,然后呼叫了一个Windows函数。在这个函数传回时,您还能保证那个变数的值还是原来那个吗?难说--很可能您呼叫的Windows函数产生了另外一个消息,并且窗口消息处理程序在处理这个消息时改变了该变量的值。这也是在编译Windows程序时,有些编译最佳化选项必须关闭的原因之一。

0 0
原创粉丝点击