Post by "Shrek", 03-22-2007, 17:44
-----------------------------------------------------
WM_PAINT消息小结
WM_PAINT是Windows窗口系统中一条重要的消息,应用程序通过处理该消息实现在窗口上的绘制工作。
窗口的Update Region
Update Region标识了一个窗口上无效、需要被重画的部分。系统使用它来来产生WM_PAINT消息。它使应用程序能够高效地更新窗口内容,因为一方面应用程序可以根据Update Region来决定哪些重画操作是必要的,哪些是不必要的,从而执行最少的重画操作;另一方面,即使重画整个窗口,也只对Update Region区产生效果,其他部分自动自动处于clip region之外。
系统何时发送WM_PAINT消息?
系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些动作都是由系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。
系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽可能地推后做,这样有利于提高绘制的效率:在两个WM_PAINT消息之间多个Invalidate调用使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送 WM_PAINT消息而不管Update Region是否为空等。
BeginPaint
BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样?程序会像进入了一个死循环一样达到惊人的CPU占用率,你会发现程序总在处理一个接一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息时,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了),BeginPaint的一个作用就是把该Update Region置为空,这样如果不调用BeginPaint,窗口的Update Region就一直不为空,如前所述,系统就会一直发送WM_PAINT消息。
BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时,BeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其返回信息里有一个标志表明窗口背景是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时,可以设置该区域是否需要被擦除背景,这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。
另外要注意的一点是,BeginPaint只能在WM_PAINT处理函数中使用。
http://hi.baidu.com/evanli/blog/item/99427f065c21097a03088164.html
Post by "Shrek", 03-22-2007, 17:48
-----------------------------------------------------
绘制和更新
在文字模式环境下,程序可以在显示器的任意部分输出,程序输出到屏幕上的内容会停留在原处,不会神秘地消失。因此,程序可以丢掉重新生成屏幕显示时所需的信息。
在Windows 中,只能在窗口的显示区域绘制文字和图形,而且不能确保在显示区域内显示的内容会一直保留到程序下一次有意地改写它时还保留在那里。例如,使用者可能会在屏幕上移动另一个程序的窗口,这样就可能覆盖您的应用程序窗口的一部分。Windows不会保存您的窗口中被其它程序覆盖的区域,当程序移开后, Windows会要求您的程序更新显示区域的这个部分。
WM_PAINT消息
Windows是一个消息驱动系统。它通过把消息投入应用程序消息队列中或者把消息发送给合适的窗口消息处理程序,将发生的各种事件通知给应用程序。Windows通过发送WM_PAINT消息通知窗口消息处理程序,窗口的部分显示区域需要绘制。
大多数Windows程序在WinMain中进入消息循环之前的初始化期间都要呼叫函数UpdateWindow。Windows利用这个机会给窗口消息处理程序发送第一个WM_PAINT消息。这个消息通知窗口消息处理程序:必须绘制显示区域。此后,窗口消息处理程序应在任何时刻都准备好处理其它 WM_PAINT消息,必要的话,甚至重新绘制窗口的整个显示区域。在发生下面几种事件之一时,窗口消息处理程序会接收到一个WM_PAINT消息:
* 在使用者移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见。
* 使用者改变窗口的大小(如果窗口类别样式有着CS_HREDRAW和CS_VREDRAW位旗标的设定)。
* 程序使用ScrollWindow或ScrollDC函数滚动显示区域的一部分。
* 程序使用InvalidateRect或InvalidateRgn函数刻意产生WM_PAINT消息。
在某些情况下,显示区域的一部分被临时覆盖,Windows试图保存一个显示区域,并在以后恢复它,但这不一定能成功。在以下情况下,Windows可能发送WM_PAINT消息:
* Windows擦除覆盖了部分窗口的对话框或消息框。
* 菜单下拉出来,然后被释放。
* 显示工具提示消息。
在某些情况下,Windows总是保存它所覆盖的显示区域,然后恢复它。这些情况是:
* 鼠标光标穿越显示区域。
* 图标拖过显示区域。
处理WM_PAINT消息要求程序写作者改变自己向显示器输出的思维方式。程序应该组织成可以保留绘制显示区域需要的所有信息,并且仅当「响应要求」-即 Windows给窗口消息处理程序发送WM_PAINT消息时才进行绘制。如果程序在其它时间需要更新其显示区域,它可以强制Windows产生一个 WM_PAINT消息。这看来似乎是在屏幕上显示内容的一种舍近求远的方法。但您的程序结构可以从中受益。
有效矩形和无效矩形
尽管窗口消息处理程序一旦接收到WM_PAINT消息之后,就准备更新整个显示区域,但它经常只需要更新一个较小的区域(最常见的是显示区域中的矩形区域)。显然,当对话框覆盖了部分显示区域时,情况即是如此。在擦除对话框之后,需要重画的只是先前被对话框遮住的矩形区域。
这个区域称为「无效区域」或「更新区域」。正是显示区域内无效区域的存在,才会让Windows将一个WM_PAINT消息放在应用程序的消息队列中。只有在显示区域的某一部分失效时,窗口才会接受WM_PAINT消息。
Windows 内部为每个窗口保存一个「绘图信息结构」,这个结构包含了包围无效区域的最小矩形的坐标以及其它信息,这个矩形就叫做「无效矩形」,有时也称为「无效区域」。如果在窗口消息处理程序处理WM_PAINT消息之前显示区域中的另一个区域变为无效,则Windows计算出一个包围两个区域的新的无效区域(以及一个新的无效矩形),并将这种变化后的信息放在绘制信息结构中。Windows不会将多个WM_PAINT消息都放在消息队列中。
窗口消息处理程序可以通过呼叫InvalidateRect使显示区域内的矩形无效。如果消息队列中已经包含一个WM_PAINT消息,Windows将计算出新的无效矩形。否则,它将一个新的WM_PAINT消息放入消息队列中。在接收到WM_PAINT消息时,窗口消息处理程序可以取得无效矩形的坐标(我们马上就会看到这一点)。通过呼叫GetUpdateRect,可以在任何时候取得这些坐标。
在处理WM_PAINT消息处理期间,窗口消息处理程序在呼叫了BeginPaint之后,整个显示区域即变为有效。程序也可以通过呼叫ValidateRect函数使显示区域内的任意矩形区域变为有效。如果这呼叫具有令整个无效区域变为有效的效果,则目前队列中的任何WM_PAINT消息都将被删除。
http://hi.baidu.com/%B0%D9%B6%C8%D3%CE%CF%B7%CA%C0%BD%E7/blog/item/ddb0b41ba94ee8198618bfa6.html
Post by "TaylorTeng", 03-30-2007, 9:20
-----------------------------------------------------
WM_PAINT消息
WndProc处理的第二个消息为WM_PAINT。这个消息在Windows程式设计中是很重要的。当视窗显示区域的一部分显示内容或者全部变为「无效」,以致于必须「更新画面」时,将由这个消息通知程序。
显示区域的显示内容怎么会变得无效呢?在最初建立视窗的时候,整个显示区域都是无效的,因为程序还没有在视窗上画什么东西。第一条WM_PAINT消息(通常发生在WinMain中呼叫UpdateWindow时)指示视窗消息处理程式在显示区域上画一些东西。
在使用者改变HELLOWIN视窗的大小后,显示区域的显示内容重新变得无效。在视窗大小改变后,就把整个视窗显示内容当成无效。然后,视窗消息处理程式将收到一条WM_PAINT消息。
在图形环境下,视窗显示区域涉及的资料量很大。因此,Windows令视窗无效,视窗讯息处理程式接收一条WM_PAINT讯息,并自动恢复其视窗的内容。
在移动视窗以致其相互重叠时,Windows不保存一个视窗中被另一个视窗所遮盖的内容。在这一部分不再被遮盖之后,它就被标志为无效。视窗讯息处理程式接收到一条WM_PAINT讯息,以更新视窗的内容。
对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始:
hdc = BeginPaint (hwnd, &ps) ;
而以一个EndPaint呼叫结束:
EndPaint (hwnd, &ps) ;
在这两个呼叫中,第一个参数都是程式的视窗代号,第二个参数是指向型态为PAINTSTRUCT的结构指标。PAINTSTRUCT结构中包含一些视窗消息处理程序,可以用来更新显示区域的内容。
在 BeginPaint调用中,如果显示区域的背景还未被删除,则由Windows来删除。它使用注册视窗类别的WNDCLASS结构的 hbrBackground栏位中指定的画刷来删除背景。在HELLOWIN中,这是一个白色备用画刷。这意味著,Windows将通过把视窗背景设定为白色来删除视窗背景。BeginPaint调用令整个显示区域有效,并传回一个「装置内容代号」。装置内容是指实体输出设备(如视讯显示器)及其装置驱动程式。在视窗的显示区域显示文字和图形需要装置内容代号。但是从 BeginPaint传回的装置内容代号不能在显示区域之外绘图,我们可以试一试。EndPaint释放装置内容代号,使之不再有效。
如果视窗消息处理程式不处理WM_PAINT讯息(这是很罕见的),它们必须被传送给DefWindowProc。DefWindowProc只是依次呼叫BeginPaint和EndPaint,以使显示区域有效。
呼叫完BeginPaint之後,WndProc接著呼叫GetClientRect:
GetClientRect (hwnd, &rect) ;
第一个参数是程式视窗的代号。第二个参数是一个指标,指向一个RECT型态的rectangle结构。该结构有四个LONG栏位,分别为left、top、 right和bottom。GetClientRect将这四个栏位设定为视窗显示区域的尺寸。left和top栏位通常设定为0,right和 bottom栏位设定为显示区域的宽度和高度(图元点数)。
WndProc除了将该RECT结构指标作为DrawText的第四个参数传递外,不再对它做其他处理:
DrawText ( hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
DrawText可以输出文字(正如其名字所表明的一样)。由於该函式要输出文字,第一个参数是从BeginPaint传回的装置内容代号,第二个参数是要输出的文字,第三个参数是 -1,指示字串是以位元组0终结的。
DrawText 最后一个参数是一系列位元旗标,它们均在WINUSER.H中定义(虽然由於其显示输出的效果,使得DrawText像一个GDI函式呼叫,但它确实因为相当高级的画图功能而成为User模组的一部分。此函式在/Platform SDK/Graphics and Multimedia Services/GDI/Fonts and Text中说明)。旗标指示了文字必须显示在一行上,水平方向和垂直方向都位於第四个参数指定的矩形中央。因此,这个函式呼叫将让字串「Hello, Windows 98!」显示在显示区域的中央。
一旦显示区域变得无效(正如在改变大小时所发生的情况一样),WndProc就接收到一个新的WM_PAINT讯息。WndProc通过呼叫GetClientRect取得变化後的视窗大小,并在新视窗的中央显示文字。 |
|