副标题#e#
在Windows API编程中,WM_PAINT是Windows窗口的一个重要动静,应用措施就是通过响应 这个动静来完成窗口的绘制。
The WM_PAINT message is generated by the system and should not be sent by an application.The system sends this message when there are no other messages in the application’s message queue
留意:WM_PAINT 动静是由系统发生,非要等应用措施的动静行列为空时才发送WM_PAINT动静 。
其实 系统会在许多的差异的机制下发送WM_PAINT动静,好比挪用UpdateWindow函数,第一次建设 窗口,改变了窗口的巨细,最大化,最小化等等。这些行动的发生都是有系统来节制的,应 用措施只是吸收动静,并处理惩罚动静。
当Window检测到窗口被包围的处所需要规复的时 候,它会向用户措施发送一个WM_PAINT动静,动静中包罗了需要规复的区域,然后由用户程 序来抉择如何规复被包围的内容。窗口进程收到WM_PAINT动静后,并不代表整个客户区都需 要被刷新,有大概客户区被包围的区域只有一小块,这个区域叫做“无效区域” ,措施只需要更新这个区域。与WM_TIMER动静雷同,WM_PAINT动静也是一个初级此外动静, 固然它不会像WM_TIMER动静一样被扬弃,但Windows老是在动静轮回空的时候才把WM_PAINT放 入个中,实际上,Windows为每个窗口维护一个“画图信息布局”,无效区域的坐 标就在个中,每当动静轮回空的时候,假如Windows发明存在一个无效区域,就会放入一个 WM_PAINT动静。
无效区域的坐标并不附带在WM_PAINT动静的参数中,在措施中有其他 要领可以获取,WM_PAINT动静只是通知措施有个区域需要更新罢了,所以Windows也不会同时 将两条WM_PAINT动静放入动静轮回中,当Windows要放入一条WM_PAINT动静的时候,假如发明 已经存在一个无效区域了,那么它只需要把新旧两个无效区域归并计较出一个无效区域就可 以了,动静轮回中照旧只需要一条WM_PAINT动静。
假如措施在WM_PAINT动静中对客户 区刷新完毕后事情并没有竣事,假如不使无效区域变得有效,Windows会在下一轮动静轮回中 继承放入一个WM_PAINT动静,而不是按照措施是否执行了刷新进程,所以措施也可以不去刷 新客户区,而是简朴地用一个ValidateRect函数直接让客户区变得有效,以此来“欺骗 ”Windows已经没有无效区域了,当Windows查抄“画图信息布局”的时候发 现没有了无效区域,也就不会继承发送WM_PAINT动静了。
那么“画图信息布局 ”怎么获取呢?BeginPaint函数的第二个参数是一个画图信息布局的缓冲区地点, windows会在这里返回画图信息布局,布局中包括了无效区域的位置和巨细,画图信息布局的 界说如下:
typedef struct tagPAINTSTRUCT { // ps
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT;
#p#副标题#e#
个中hdc字段是窗口的设备情况 句柄,rcPaint字段是一个RECT布局,它指定了无效区域矩形的对角极点,fErase字段假如为 非零值,暗示Windows在发送WM_PAINT动静前已经利用配景致擦除了无效区域,后头3个字段 是Windows内部利用的,应用措施不必去剖析他们。
摘自《Windows情况下32位汇编语 言措施设计》
大大都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老是生存它所包围的显示区域,然后规复它。这些环境是:
鼠标光标穿越显 示区域。
图标拖过显示区域。
#p#分页标题#e#
处理惩罚WM_PAINT动静要求措施写作者改变本身向 显示器输出的思维方法。措施应该组织成可以保存绘制显示区域需要的所有信息,而且仅当 「响应要求」-即Windows给窗口动静处理惩罚措施发送WM_PAINT动静时才举办绘制。假如措施在 其它时间需要更新其显示区域,它可以强制Windows发生一个WM_PAINT动静。这看来好像是在 屏幕上显示内容的一种舍近求远的要领。但您的措施布局可以从中受益。
1. 系统何 时发送WM_PAINT动静?
系统会在多个差异的机缘发送WM_PAINT动静:当第一次建设一 个窗口时,当改变窗口的巨细时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗 口时,等等,这些行动都是由 系统打点的,应用只是被动地吸收该动静,在动静处理惩罚函数中 举办绘制操纵;大大都的时候应用也需要可以或许主动激发窗口中的绘制操纵,好比当窗口显示 的数据改变的时候,这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。 InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的动静 行列没有其他动静时,假如窗口的Update Region不为空时,系统就会自动发生WM_PAINT动静 。
系统为什么不在挪用Invalidate时发送WM_PAINT动静呢?又为什么非要等应用动静 行列为空时才发送WM_PAINT动静呢?这是因为系统把在窗口中的绘制操纵看成一种低优先级 的操纵,于是尽 大概地推后做。不外这样也有利于提高绘制的效率:两个WM_PAINT动静之间 通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个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是否为空等。
2. BeginPaint
本日在处理惩罚WM_PAINT动静 时发生了一个初级的错误,并搞的我花了快一个小时才找到原因。我在处理惩罚动静时,没有使 用BeginPaint和EndPaint这对函数,功效我其余的动静弹不出来,窗口拖动时,不断闪烁( 其实那就是重绘)。厥后照旧在MSDN上找到了谜底,现将原话贴出来。(在MSDN的The WM_PAINT Message标题中)
BeginPaint sets the update region of a window to NULL. This clears the region, preventing it fromgenerating subsequent WM_PAINT messages. If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty. In all cases, an application must clear the update region before returning from the WM_PAINT message.
BeginPaint函数的浸染就是将窗口需要重绘的区域配置为空(也就是 Update Region置空)。在正常环境下,我们吸收到了WM_PAINT动静后,窗口的Update Region都长短空的(假如为空就不需要发送WM_PAINT动静了)。而当你响应这个动静的时候 又不挪用BeginPaint来清空,窗口的Update Region就一直长短空的,系统就会一直发送 WM_PAINT动静。这样就形成了一个处理惩罚WM_PAINT动静的死轮回。这就是我呈现错误的原因, 初级错误。
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动静了。
#p#分页标题#e#
虽然关于 WM_PAINT动静尚有许多的常识需要进修。别的 要留意的一点是,BeginPaint只能在WM_PAINT处理惩罚函数中利用,而且在挪用了BeginPaint函 数后,不要健忘了挪用EndPaint函数,他们但是一对的。
3.重画函数 InvalidateRect,UpdateWindow, RedrawWindow的区别
InvalidateRect是通过线程 的动静行列来发送刷新动静,是最常用的。
UpdateWindow是直接挪用窗口函数当即响 应刷新动静,使窗口刷新动静优先被响应(动静行列中假如没有WM_PAINT动静就什么都不执 行),一般是在ShowWindow之后挪用。
RedrawWindow相当于先挪用InvalidateRect, 紧接着又挪用UpdateWindow,另外RedrawWindow还提供了一些前两者没法做到的成果。
增补几点:
1.WM_Paint 是一个被动动静,不能通过普通的要领简朴的 sendmessage WM_paint 了事这是不可的;但通过动静由措施员激发不是不行能;通过几个非凡 的常数可以做到,不外要到delphi下找
2.sendmessage 可以将动静发送到动静行列;但 windows会自动判定是否存在无效的绘图区域;假如存在无效的绘图区域,则大概会重画,反之 则弃用该动静.
3.可以利用 InvalidateRect 等几个APi将屏幕上任意一个个矩形区域 配置为无效区域,在UpdateWindow后挪用后,windows会自动查找是否存在无效,并重画,该矩形 区域;