我的程序有两个窗口,前景窗口用SetLayeredWindowAttributes使得除了控件之外的部分都是全透明的,背景窗口是独立的窗口,用UpdateLayeredWindow去把一张PNG图贴在背景窗体上,这样看上去就像是前景窗口的背景一样。MSDN上说调用了UpdateLayeredWindow之后,layered window就不会再收到WM_PAINT消息了,但是我碰到几个奇怪的现象。1,当两个窗体正确显示出来之后,如果用户调整屏幕分辨率或者按了WIN+D键,背后的layered window开始持续不断收到WM_PAINT消息,我在背景窗口的OnPaint里面加了TRACE,发现有源源不断无穷无尽的调试信息,所以我在它的OnPaint里面又调用了UpdateLayeredWindow去重绘背景的半透明PNG图。2,如果用户按了WIN+D,虽然前景窗口依然是可见的,但是从SPY++里可以看到,前景窗口再也接收不到任何windows标准消息(即所有WM_XXX消息),但是前景窗口还能接收到其他模块发来的消息,如鼠标钩子消息(是我自定义的WM_USER+XXX类型的消息),所以我可以肯定此时前景窗口的消息循环是正常的,但不知为何,Windows OS不再向前景窗口发送任何消息,包括WM_TIMER消息。3,如果在背景窗口中不处理WM_PAINT消息,抑或在背景窗口的OnPaint()中调用了UpdateLayeredWindow之后再调用Default()或父类CWnd的OnPaint(),则一切正常,前景窗口能继续收到Windows的消息,背景窗口看起来也绘制的很正常(而且背景窗体只收到一个WM_PAINT),换言之背景窗口即使又再次收到了WM_PAINT,也无须做任何重绘处理?4,如果在背景窗口的OnPaint中不调用父类CWnd::OnPaint,则WM_PAINT消息则源源不断地发过来。我的问题:
1,是什么导致背景的layered window在调用了UpdateLayeredWindow之后又开始收到WM_PAINT消息?WIN+D按下之后发生了什么?
2,用户按下WIN+D之后,背景窗口开始接收到WM_PAINT消息,为什么我不需要处理之并重绘layered window呢?事实证明即使我此时不重绘,背景窗口看起来也正常。
3,为什么如果不在背景窗口的OnPaint中调用父类CWnd::OnPaint,会影响到前景窗口收不到任何消息?按理说前景窗口和背景窗口都有各自的消息循环才对,为何背景窗口会影响到前景窗口?

解决方案 »

  1.   

    1,任何时候,在调用了UpdateLayeredWindow之后,Windows仍然向分层窗口发送WM_PAINT消息,分层窗口也会收到WM_PAINT,只不过DC绘制已经被UpdateLayeredWindow接管,因此 OnPaint中的绘制代码就不起作用了。2,用户按下WIN+D之后,背景窗口开始接收到WM_PAINT消息,不需要处理之并重绘layered window,DC绘制已经被UpdateLayeredWindow接管。你只需要在UpdateLayeredWindow的内存DC中绘制,并在需要的时候调用UpdateLayeredWindow,把内存DC更新到窗口就行。3,估计:Win+D是使得所有窗口最小化并显示桌面。窗口最小化,一般来说是不会进行窗口的重绘,虽然你的窗口还显示,也不会触发消息。
      

  2.   

    已经证实WM_PAINT消息是USER的bug。
      

  3.   

    答:
    1。理解有误,UpdateLayeredWindow不接管绘制,只是使用此方法绘制的内容会在以后每次刷新时由系统更新,其它的内容比如控件的显示并没有被接管,win+D之后要重新绘制子窗口(前)。
    2。同上,UpdateLayeredWindow绘制的内容及使用UpdateLayeredWindow做出的变更由系统在每次刷新时自动应用到界面上;
    3。WM_PAINT 需要用CWnd::OnPaint来结束绘制,(如果不用这句,可以用CPaintDC dc(this); 也可使绘制状态返回)。
      

  4.   

    好好看看 UpdateLayeredWindow 的参数。
    HDC hdcSrc,
    HDC hdcDst,所有DC更新,都由 UpdateLayeredWindow 来更新。其实就是自己在hdcDst(内存DC)中自己画,画好以后,调用 UpdateLayeredWindow ,把自己画好的 内存DC拷贝到窗口上。每次刷新,OnPaint 中的CPaintDC dc(this) 不再起作用。
      

  5.   

    你们理解的都部分正确。
    其实是这样的,首先UpdateLayeredWindow绘制的内容是被OS缓存起来了,需要显示的时候,OS会调用缓存的内容来绘制layered window。
    其次,那个WM_PAINT是USE32的一个bug,只要丢给DefWindowProc或者调用一下BeginPaint/EndPaint就可以了,否则的话会持续不断收到WM_PAINT消息。
      

  6.   

    LZ,你所说的BUG 一说 出处在哪里?(真心求源,不抬杠),我也学习学习。
    没有研究过WM_PAINT的内幕,我只是简单地认为:这个消息一定是个闭环,要在系统的某一层次上返回一个状态,系统才会罢休。处理方式如你我所说就是这两种(CPaintDC封装有BeginPaint/EndPaint,DefWindowProc亦会走向CWnd::OnPaint)。
      

  7.   

    先说你的第二个问题,你理解正确,不过不是返回状态,而是清除该窗口的Update region。
    第一个问题,我请教了Windows组的SDE,他说是一个“Old USR bug”,丢给DefWindowProc即可,不需要在此重绘layered window。