消息处理机制-WINDOWS程序设计的难点(摘自Windows程序设计开发指南)

本文探讨了Windows编程中的难点,包括程序如何响应操作系统调用、队列化与非队列化消息的区别,以及如何确保程序在多任务环境下保持响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序设计的难点:

一、别调用我,我会调用您

程序编写者已经熟悉了使用操作系统调用的做法。例如,C程序编写者使用fopen函数打开文件。fopen函数最终通过调用操作系统来打开文件,这一点问题也没有。但是Windows不同,尽管Windows有1000个以上的函数可供程序调用,但Windows也调用使用者程序,比如前面定义的窗口消息处理程序WndProc。窗口消息处理程序与窗口类别相关,窗口类别是程序调用RegisterClass注册的。依据该类别建立的窗口使用这个窗口消息处理程序来处理窗口的所有消息。Windows通过调用窗口消息处理程序对窗口发送消息。在第一次建立窗口时,Windows调用WndProc。在窗口关闭时,Windows也调用WndProc。窗口改变大小、移动或者变成图标时,从菜单中选择某一项目、挪动卷动列、按下鼠标按钮或者从键盘输入字符时,以及窗口显示区域必须被更新时,Windows都要调用WndProc。所有这些WndProc调用都以消息的形式进行。在大多数Windows程序中,程序的主要部分都用来处理消息。Windows可以发送给窗口消息处理程序的消息通常都以WM开头的名字标识,并且都在WINUSER.H表头文件中定义。实际上,从程序外调用程序内的例程这一种做法,在传统的程序设计中并非前所未闻。C中的signal函数可以拦截Ctrl-C中断或操作系统的其它中断。为MS-DOS编写的老程序中经常有拦截硬件中断的程序代码。但在Windows中,这种概念扩展为包括一切事件。窗口中发生的一切都以消息的形式传给窗口消息处理程序。然后,窗口消息处理程序以某种方式响应这个消息,或者将消息传给DefWindowProc,进行内定处理。在HELLOWIN中,窗口消息处理程序的wParam和lParam参数除了作为传递给DefWindowProc的参数外,不再有其它用处。这些参数给出了关于消息的其它信息,参数的含义与具体消息相关。让我们来看一个例子。一旦窗口的显示区域大小发生了改变,Windows就调用窗口的窗口消息处理程序。窗口消息处理程序的hwnd参数是改变大小的窗口的代号(请记住,一个窗口消息处理程序能处理依据同一个窗口类别建立的多个窗口的消息。参数hwnd让窗口消息处理程序知道是哪个窗口在接收消息)。参数message是WM_SIZE。消息WM_SIZE的参数wParam的值是SIZE_RESTORED、 SIZE_MINIMIZED、SIZE_MAXIMIZED、SIZE_MAXSHOW或SIZE_MAXHIDE (在WINUSER.H表头文件中分别定义为数字0到4)。也就是说,参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏。 lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。WINDEF.H中提供了帮助程序编写者从 lParam中取出这两个值的宏,我们将在 下一章 说明这个宏。有时候,DefWindowProc处理完消息后会产生其它的消息。例如,假设使用者运行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中的消息循环终止,然后程序结束。

二、队列化消息与非队列化消息

我们已经谈到过,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程序时,有些编译最佳化选项必须关闭的原因之一。在许多情况下,窗口消息处理程序必须保存它从消息中取得的信息,并在处理另一个消息时使用这些信息。这些信息可以储存在窗口的静态(static)变量或整体变量中。当然,读者将在下面几章对此有一个更清楚的了解,因为窗口消息处理程序将处理更多的消息。

三、行动迅速

Windows 98和Windows NT都是优先权式的多任务环境。这意味着当一个程序在进行一项长时间工作时,Windows可以允许使用者将控制切换到另一个程序中。这是一件好事,也是现在的Windows优越于以前16位Windows的地方。然而,由于Windows设计的方式,这种优先权式多任务并不总是以您希望的样子工作。例如,假设您的程序花费一分钟左右来处理某一个消息。是的,使用者可以将控制切换到另一个程序,但是却无法对您的程序进行任何动作。使用者无法移动您的程序窗口、缩放它、最小化、关闭它、什么都不能做。这是因为您的窗口消息处理程序正忙于进行一项长时间的作业。表面上并不是窗口消息处理程序在运行它自己的移动和缩放操作,但实际上确实是它在做。这就是DefWindowProc部分的工作,它必须被考虑为您的窗口消息处理程序的一部分。如果您的程序在处理某些消息时需要长时间的作业的话,可以选择我在 第二十章 里描述的那些方法来做得更有优雅一些。即使是在优先权式多任务环境中,也不应该让您的程序呆在屏幕上一动不动。这会让使用者讨厌的,他们会认为您的程序中有bug、不标准的动作,说明文件没写好。最好让使用者觉得程序只停了一下子就把全部消息中快速料理完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值