出处:Windows程序设计 第五版 珍藏版 Page0162
在任何一个图形系统中,总存在这样一个有趣的程序,即简单地使用随机的尺寸和颜色不停地绘制一系列图像,例如,随机大小和颜色的矩形。在Windows中可以创建这样的一个程序,但是这样并不像想象的那样容易。我希望你能够意识到,不能在处理WM_PAINT消息中简单地使用while(TRUE)循环。当然,这样做会奏效,但是这样做的结果是,程序将停止对其他消息的处理,而且程序不能退出或者最小化。
一种可接受的方式是设置一个向你的窗口函数发送WM_TIMER消息的Windows计时器。(我将在第8章介绍计时器。)对每个WM_TIMER消息,可以调用GetDC函数获取设备环境,然后绘制一个随机矩形,接着调用ReleaseDC函数释放设备环境。但是那样做又会使程序失去一些趣味性,因为程序不能很快地绘制随机矩形。必须等待每个WM_TIMER消息,那样会依赖于系统时钟的精度。
在Windows中有很多的“空闲时间”,在这期间所有的消息队列都是空的,Windows就在等待键盘或者鼠标的输入。那么能否在空闲期间从某种程度上获取控制并绘制随机矩形,而一旦有消息加载到程序的消息队列,就释放控制呢?这正是PeekMessage函数的“用武之地”。下面是PeekMessage函数调用的一个例子:
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
函数的前4个参数(一个是指向MSG结构的指针,一个是窗口句柄,另外两个值表示信息范围)与GetMessage函数相同。设置第二、三、四个参数为NULL或者0,表示我们想使用PeekMessage函数返回程序中所有窗口的所有消息。如果要删除消息队列中的消息,可以把PeekMessage函数的最后一个参数设置为PM_REMOVE。如果不想删除,就设置为PM_NOREMOVE。这就是PeekMessage名字的意思,它是“偷看”而不是“获得”。它允许一个程序检查程序队列中的下一个消息,而不是真实地获得并删除它看到的消息。
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{
/*other program lines to do some work*/
}
}
return msg.wParam;
如果PeekMessage函数返回TRUE,那么消息会正常执行。如果返回FALSE,那么程序可以在返回给Windows控制之前做些事情(如显示另一个随机矩形)。
(尽管Windows文档中指出不能使用PeekMessage函数从消息队列中删除WM_PAINT消息,但是这并没有什么问题。毕竟,GetMessage函数其实也不能从队列中删除WM_PAINT消息。使客户区的无效区域变成有效是从队列删除WM_PAINT消息的唯一办法,可以使用ValidateRect、ValidateRgn或者成对的BeginPaint和EndPaint函数来完成。如果使用PeekMessage函数从消息队列中获取WM_PAINT消息后,按照正常的方式对它进行处理,就不会有任何问题。但使用下面的代码来清除消息队列中的所有消息是不允许的:
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
这条语句表示从你的消息队列中删除WM_PAINT消息之外的所有消息。如果WM_PAINT在队列中,你将永远陷于while循环无法终止。)
PeekMessage函数在早期版本的Windows中比Windows 98中重要得多。这是因为16位版本的Windows使用非抢占式多任务系统(我将在第20章讨论)。Windows自带的Terminal程序使用PeekMessage函数循环检查从通信端口接收到的数据。打印机管理程序也使用这项技术来打印,其他的Windows打印程序通常也使用一个PeekMessage函数的循环。在抢占式多任务的Windows 98中,应用程序可以建立多个线程,详情可参见第20章。
不管怎么样,有了PeekMessage函数,就可以使用这个函数来写不停显示随机矩形的程序。