在图形系统中,有这么一个「永远」有人执行的有趣程序,它简单地使用随机的大小和色彩绘制一系列矩形。您可以在Windows中建立一个这样的程序,但是它并不像乍看起来那样容易编写。我希望您能认识到,您不能简单地在WM_PAINT消息中使用一个while(TRUE)循环。当然,它能够执行,但是程序将停止对其他消息的处理,同时,这个程序不能中止或者最小化。
一种可以接受的方法是设定一个Windows定时器,给窗口程序发送WM_TIMER消息(我将在 第八章中讨论定时器)。对于每条WM_TIMER消息,您使用GetDC取得一个设备内容,画一个随机的矩形,然后用ReleaseDC释放设备内容。但是这样又降低了程序的趣昧性,因为程序不能尽可能快地画随机矩形,它必须等待WM_TIMER消息,而这又依赖于系统时钟的分辨率。
在Windows中一定有很多「闲置时间」,在这个时间内,所有消息队列为空,Windows只停在一个小循环中等待键盘或者鼠标输入。我们能否在闲置时间内获得控制,绘制矩形,并且只在有消息加入程序的消息队列之后才释放控制呢?这就是PeekMessage函数的目的之一。下面是PeekMessage呼叫的一个例子:
PeekMessage (&msg, NULL, 0, 0, PM_REMOVE) ;
前面的四个参数(一个指向MSG结构的指针、一个窗口句柄、两个值指示消息范围)与GetMessage的参数相同。将第二、三、四个参数设定为NULL或0时,表明我们想让PeekMessage传回程序中所有窗口的所有消息。如果要将消息从消息队列中删除,则将PeekMessage的最后一个参数设定为PM_REMOVE。如果您不希望删除消息,那么您可以将这个参数设定为PM_NOREMOVE。这就是为什么Peek_Message是「偷看」而不是「取得」的原因,它使得程序可以检查程序的队列中的下一个消息,而不实际删除它。
GetMessage不将控制传回给程序,直到从程序的消息队列中取得消息,但是PeekMessage总是立刻传回,而不论一个消息是否出现。当消息队列中有一个消息时,PeekMessage的传回值为TRUE(非0),并且将按通常方式处理消息。当队列中没有消息时,PeekMessage传回FALSE(0)。
这使得我们可以改写普通的消息循环。我们可以将如下所示的循环:
while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ;
替换为下面的循环:
while (TRUE) { if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break ; TranslateMessage (&msg) ; DispatchMessage (&msg) ; } else { // 完成某些工作的其它行程序 } } return msg.wParam ;
注意,WM_QUIT消息被另外挑出来检查。在普通的消息循环中您不必这么作,因为如果GetMessage接收到一个WM_QUIT消息,它将传回0,但是PeekMessage用它的传回值来指示是否得到一个消息,所以需要对WM_QUIT进行检查。
如果PeekMessage的传回值为TRUE,则消息按通常方式进行处理。如果传回值为FALSE,则在将控制传回给Windows之前,还可以作一点工作(如显示另一个随机矩形)。