ShowWindow, UpdateWindow, SendMessage, PostMessage, PeekMessage, GetMessage

ShowWindow与UpdateWindow

CWnd::UpdateWindow()

       In MSDN:Updates the client area by sending a WM_PAINT message if the update region is not empty.The UpdateWindow member function sends a WM_PAINT messagedirectly,bypassing the application queue. If the update region is empty, WM_PAINT is not sent.

       首先,ShowWindow本身是不会产生重画消息的,它的作用仅仅是把窗口显示出来。不过,当窗口显示的时候,Windows会自动探测窗口的内容是否需要重画、以及需要重画的区域组成,比如你的窗口位置直接在屏幕外,或者你的窗口被别的窗口完全挡住,当然就不需要重画,如果你的窗口只露出一部分,那么就只有这一部分需要重画。这个过程与你移动窗口、切换窗口的时候Windows所做的事情是一样的——自动判定你的窗口有哪一部分原来不显示而现在需要显示,然后对这部分区域调用InvalidateRect()。这个函数的作用并不是立刻重画这些区域,而是对这些区域做上标记。多次调用这个函数,新标记的区域会与以前标记的区域合并。

       之后,当你的消息队列完全空了的时候,假若windows又发现你窗口所标记的重画区域不为空,那么Windows就在你的消息队列里放一个WM_PAINT消息,让你重画。根据这一流程可知,假若我们的消息队列一直很忙的话,那么窗口是没机会获得WM_PAINT消息的。其次,假定消息队列里有若干个消息,每个都导致一部分窗口区域需要重画,那么最后只会重画一次,只不过重画的范围是几个区域的合并。再有,某些特殊情况下,有可能会不希望窗口被重画、或者至少其中某一部分不要重画,那么你可以在消息队列被取空之前(尚未发出 WM_PAINT),用ValidateRect把窗口的某一部分乃至全部都取消标记。如果所有以前被标记的部分全被你取消掉了,那么等消息队列空了以后,也不会再有WM_PAINT发出了。

       当你在处理WM_PAINT消息进行重画的时候,BeginPaint的一个重要作用,就是在它返回的DC里,用原来标记的区域制作一个剪裁区域(ClipRegion),从而使你的所有重画操作都被限定在这一个区域中。这是一个很重要的特性。举例来说,假若你的窗口用一张位图作为背景,处理 WM_PAINT的时候用BitBlt之类的方法往屏幕上贴图。如果某一次有一个别的窗口仅仅盖住了你窗口的一个小角,当它拿开的时候,如果没有剪裁区域的话,那么就会对整个窗口贴图,这不仅很慢,而且会引起你窗口中的各个子窗口的闪烁。但有了剪裁区域的话,你的代码虽然还是在对整个窗口贴图,但实际上只有位于剪裁区域内的那部分操作有效,其它的都被Windows放弃了,所以速度会快得多。有时我们自己也需要更新窗口的显示内容,这时候也是通过调用 InvalidateRect来做。不过,很多人在这种情况下习惯于将整个窗口统统Inalidate,这样做倒是很方便,不过这是一个很不好的习惯。除非你需要更新的内容波及到整个窗口,否则应该仅仅把需要改变的那部分Invalidate。

      上面说的剪裁区域仅仅是BeginPaint的一个作用,BeginPaint还有其它作用,都是跟重画这个任务紧密连接的,因此,在响应WM_PAINT消息的时候,必须使用BeginPaint所获取的DC句柄来画图,绝不能用GetDC等其它方式。相对应的,这个句柄也必须使用EndPaint来释放。如果在响应WM_PAINT的时候没有调用BeginPaint和EndPaint(例如用 GetDC和ReleaseDC来画图),其中一个副作用就是:重画区域的标记不会被取消。于是当你响应完这一个WM_PAINT之后,Windows会发现你的窗口还有区域被标记为重画,于是再次发出WM_PAINT,于是你就永无休止地重画下去了。

       从上述可知,单纯一个ShowWindow,照样会正确重画窗口内容,只不过重画是在消息队列取空之后。有时我们希望窗口被立即重画,而不是去等待那个不确定的消息队列,此时就需要用到UpdateWindow。这个函数的作用只有一个:假若当前被标记为重画的区域存在(不存在的话它什么也不做),那么立刻让Windows使用SendMessage的方式来对你的窗口发送WM_PAINT。


SendMessage与PostMessage

       说到这里,就要说一下SendMessage与PostMessage的区别了。PostMessage是把消息放到消息队列尾部,然后通过程序的消息环逐个从消息队列里取出来进行处理。SendMessage却不是这样,它实际上根本不经过消息队列。对SendMessage的处理分两种情况:

       1、由本线程发出的SendMessage,例如在自己的消息处理过程中调用UpdateWindow,从而发出的WM_PAINT。对于这种情况,SendMessage实际上直接调用你窗口的消息处理函数。也就是说,在进行消息处理的时候对本窗口SendMessage,实际上是递归调用。整个过程是:消息环取出消息A(假定A是PostMessage放进消息队列的)-> 处理消息A -> SendMessage对本线程的窗口发送消息B -> 处理消息B -> 继续处理消息A -> 消息环再取下一个消息。

       2、由另一个线程发出的SendMessage。此时当然不能直接调用了。这种情况下,windows会把发送消息的线程挂起,然后,当接收消息的线程调用PeekMessage或者GetMessage的时候,这两个函数会立刻调用你窗口的消息处理函数,直到处理完毕(此时发送线程的 SendMessage才返回),PeekMessage或者GetMessage才会去检查消息队列,并从中取出一个返回。

       总之,不论哪种情况,SendMessage发送的消息,跟消息队列均没有任何关系,而且也不通过消息环执行(前者是直接调用,后者是在PeekMessage或GetMessage函数内部调用)。


PS: BeginPaint、InvalidateRect两个函数要注意

参考文章:

http://topic.csdn.net/u/20100818/23/b30e7e3d-2969-45ec-9054-1e98cca442e5.html


PeekMessage与GetMessage
相同点:
       PeekMessage函数与GetMessage函数都用于查看应用程序消息队列,有消息时将队列中的消息派发出去。

不同点:
       无论应用程序消息队列是否有消息,PeekMessage函数都立即返回,程序得以继续执行后面的语句(无消息则执行其它指令,有消息时一般要将消息派发出去,再执行其它指令)。
       GetMessage函数只有在消息对立中有消息时返回,队列中无消息就会一直等,直至下一个消息出现时才返回。在等的这段时间,应用程序不能执行任何指令。(从他们的不同点上来看,PeekMessage函数有点像“乞丐行乞”,有你就施舍点,没有也不强求。GetMessage函数有点像“强盗打劫”,有你得给,没有我就等你什么时候有了再给,这段时间我什么都不干,我就等你。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值