初步介绍:
这个主要说明,when:什么时间,什么时刻,最适合来进行GDI操作已经其中的处理机制。
- 客户区的刷新:
- 在DOS系统上:
- 在DOS操作系统中变成的时候,程序把文字或图形输出到屏幕上,在输出新的内容之前,这些内容总是保留在屏幕原处,这些内容总是保留在屏幕原处。
- 这些内容会被意外覆盖的唯一情况是激活一个TSR程序,但在TSR程序在退出之前有义务恢复原来的屏幕,如果它无法恢复屏幕的内容,那么这是它的责任,我们不会再自己的程序中去考虑屏幕内偶然无缘无故消失这中情况,所以可以把屏幕看成是应用程序私有。
- 如果程序输出的内容过多,如显示dir文件内容,这时用户根本无法看清快速上翻的屏幕,这时程序可以设计一个参数来暂停一下。如果用户想看看已经滚出屏幕的内容,那可对不起,程序只能再执行一遍。
- 所以,对于DOS程序来说,程序想什么时候输出信息那就什么时候,根本不存在When这个问题。
- 在Windows系统上:
- 在Windows中,屏幕是多个程序“公用”的,用户程序不要指望输出到屏幕中的内容经过一段时间还会保留在那里,它们可能被别的东西覆盖,如其他窗口,鼠标箭头或者下拉的菜单等。
- 在Windows中,恢复被覆盖内容的责任大部分属于用户程序自己,理由很简单:Windows是个多任务的操作系统,假如程序B覆盖了程序A的窗口内容,覆盖掉的内容由程序B负责恢复的话,它就必须保存它覆盖掉的内容,但是在它将保存的恢复之前,程序A也在运行,并可能在程序B恢复以前已经向它最的窗口输出新的内容,结果当程序B恢复它保存在窗口内容时,保存的内容是可能过时的。(DOS不同,TSR程序激活的时候,用户程序是被挂起的)
- 所以,最好的办法就是让程序A决定如何来进行恢复。
- WM_PANT消息机制:
- Windows系统采用的方法是:当 Windows 检测到窗口被覆盖的地方需要回复的时候,它会向用户程序发送一个WM_PANT消息,消息中包括了需要恢复的区域,然后由用户程序来决定如何恢复被覆盖内容。
- 如果程序因为忙于处理其他事务以至于无法及时响应WM_PANT消息,那么窗口客户区原先被覆盖的地方可能会被Windows暂时化成一块白色(或者背景色)的居心,或者就是保留被覆盖的情形,直到程序有时间去响应WM_PANT消息为止。我们常常可以看到这种情况发生在死锁程序的客户区中,这就是因为死锁的程序无法响应WM_PANT消息来恢复客户区造成的。
- 所以,对于“when”这个问题,答案是:程序应该在Windows要求的时候绘画客户区,也就是在收到WM_PANT消息的时候,如果程序需要主动刷新客户区,那么可以通过调用 InvalidateRect 等函数引发一块WM_PANT消息,因为在WM_PANT 消息中刷新客户区的代码必须存在。
- GDI 程序的结构:
- 对于Win32程序来说,WM_PANT 消息随时可能发生,这就意味着,程序在任何时刻都应该知道如何恢复整个或局部客户区中以前输出的内容。
- Windows消息的两种处理结构:
- 两种结构介绍:
- 第一种:直接在 窗口过程,处理WM_PANT时,编写一个 绘画子程序,来实现窗口绘画并刷新到整个客户区。
- 第二种:来 窗口过程,处理 WM_COMMAND消息时,将处理结果(要显示的图像结果)发送到缓冲区, 然后处理 WM_PANT 时,直接通过缓冲区来刷新区域。

- 两种程序结构对比:
- 如果程序功能比较简单,则采用第一种结构,这种结构适用于功能模块很小且执行速度很快的情况。
- 但是,对于要显示的内容,有时是非常复杂的,如圆周率计算可能要几个小时,这时直接计算肯定不现实,还有很多复杂的情况,1s的等待时间都不行。所以,在这种情况下,后台计算结果保存在缓冲区,然后由操作系统从缓冲区中拿,进行这种操作的时间可以小到忽略不计。
- 探讨WM_PANT消息:
- 当客户区被覆盖并重新显示的时候,Windows并不是在所有的情况下发送WM_PANT消息,下面是几种不同的情况:
- 当鼠标光标移动到窗口客户区,以及图标拖过客户区这两种情况,Windows总是自己保存被覆盖的区域并在以后恢复它,并不需要发送WM_PANT消息通知用户程序。(这个操作频繁,同时往往与用户程序无关,是操作系统层面上的)
- 当窗口客户区被自己的下拉式菜单覆盖,或者被自己弹出的对话框覆盖后,Windows会尝试保存被覆盖的区域并在以后覆盖它,如果因为某种原因无法保存并恢复的话,Windows会自动发送要一个WM_PANT消息通知程序。
- 别的情况造成窗口造成的窗口的一部分从不可见状态变成可见,如程序从最小化的状态恢复、其他的窗口覆盖客户去后移开,用户改变了窗口的大小和用户按动滚动条等,在这些情况下,Windows会向窗口发送 WM_PANT 消息。
- 无效区域:
窗口过程受到WM_PANT消息后,并不代表整个客户区都需要被刷新,有可能客户区被覆盖的区域只有一小块,这个区域就叫做“无效区域”,程序只需要更新这个区域。 - WM_PANT消息级别:
与 WM_TIMER 类似,WM_PANT 消息也是一个低级别的消息,虽然它不会像WM_TIMER消息一样被丢弃,但WIndows总是在消息循环为空的时候才把WM_PANT 放入其中,实际上,Windows为每个窗口维护一个“绘图信息结构”,无效区域的坐标就是在其中,每当消息循环空的时候,如果Windows发现存在一个无效区域,就会放入WM_PAINT消息。 - 无效区域与WM_PANT消息:
- 无效区域的坐标并不附带在 WM_PANT 消息的参数中,在程序中有其他方法可以获取,WM_PANT 消息只是通知程序有个区域需要更新而已,所以Windows也不会同时将两条 WM_PANT 消息放入消息循环,当Windows要放入一条WM_PANT消息的时候,如果发现已经存在一个无效区域了,那么它只需要把新旧两条无效区域合并计算出一个新的无效区域,消息循环中还是只需要一条WM_PANT消息。
- 由于存在“无效区域”这一个机制,所以程序在 WM_PANT 消息中对客户区刷新完毕后工作并没有结束,如果不使无效区域变得有效,Windows会存在下一轮消息循环继续放入WM_PANT消息。也是正因为 WIndows 仅仅 根据是否存在“无效区域”,而不是根据程序是否执行了刷新过程,所以程序也可以不去刷新客户区,二是简单地是哟一个 ValidateRect 函数直接让客户区变得有效,以此来“欺骗”Windows已经没有无效区域了,当Windows检查”绘图信息结构“的时候发现没有了无效区域,也就不会继续发送WM_PANT消息了。
- WM_PANT 消息的一般处理流程是:
- 读者可以发现中间没有调用 ValidateRect 来使无效区域变得有效,这时因为 BeginPaint 函数和 EndPaint 函数韩寒有这个功能,如果不是以这两个函数当做消息处理代码的头尾的haul,那么在 WM_PANT 消息返回的时候必须调用 ValidateRect 函数。
- PAINTSTRUCT 结构:
- hdc 指定了 窗口的设备环境句柄(之后会讲到)
- rcPaint 字段是一个 RECT 结构,它制定了无效区域矩形的对角顶点,fErase字段如果为非零值,表示 Windows 在发送 WM_PAINT 消息前已经用背景色擦出了无效区域。
- 后面3个字段是Windows内部使用的,应用程序不必去理会。