3.2.2 队列消息和非队列消息

   摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P60

        前面提过 Windows 将消息发送给一个窗口,意思是说 Windows 调用了该窗口的窗口过程。但是,一个 Windows 程序同时还具有一个消息循环用户从消息队列中检索和分发消息,其中检索消息是通过调用 GetMessage 实现的,而分发消息是通过调用 DispatchMessage 而实现的。

        那么,是 Windows 程序捕获到消息(非常类似于字符模式程序对键盘输入的捕获)然后将这些消息转送到某个目的地呢?还是直接从程序外部接收消息呢?答案是两者皆有。

        消息既可以是“队列消息”,也可以是“非队列消息”。队列消息是指那些由 Windows 放入程序的消息队列中的消息。在程序的消息循环中,消息被检索,然后被投递到窗口过程中。非队列消息则是由Windows 对窗口过程的直接调用而产生的。我们一般说队列消息被“投递”(post)到消息队列中,而非队列消息则是被“发送”(send)到窗口过程。无论在哪些情形下,窗口过程都会为窗口获取所有消息——无论是队列消息还是非队列消息。因此,窗口过程实际上是窗口的“消息中心”

        队列消息主要由用户的输入产生,主要形式为按键消息(例如 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 调用才将控制权返还给窗口过程。

        这就意味着窗口过程必须是可重入的(reentrant)。在大多数情形下,这并不会带来什么问题,但是对此必须做到心中有数。例如,假定在窗口过程处理某条消息期间,你对静态变量进行了设置,接着又调用了一个 Windows 函数。当该函数返回时,你能确保这个变量仍然跟先前一样吗?我们的确无法保证这一点,因为如果你所调用的特定 Windows 函数产生了另外一条消息,且窗口过程在处理第二条消息期间对该变量进行了修改,则该变量的状态一定会发生改变。而这也是我们在编译 Windows 程序时需要将某些编译优化关闭的原因之一。

        在许多情况下,窗口过程必须保留其从消息中获取的信息,并在处理其他消息时使用该信息。这种信息必须保存在窗口过程所定义的静态变量中,或保存在全局变量中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值