beginpaint()和getdc()都返回HDC,那么它们有什么区别?


 

这是个windows编程问题。
第一种情况显示出来的字很正常。

case WM_PAINT:
           gdc = GetDC (hwnd);
           TextOut (gdc, 0, 0, s, strlen (s));
           ReleaseDC (hwnd, gdc);
break;


 第二种情况显示的字不停闪烁。

case WM_PAINT:
           gdc = BeginPaint (hwnd, &ps);
           TextOut (gdc, 0, 0, s, strlen (s));
           EndPaint (hwnd, &ps);
break;

 

请教两种函数的作用?

BeginPaint() 和EndPaint() 可以删除消息队列中的WM_PAINT消息,并使无效区域有效。
GetDC()和ReleaseDC()并不删除也不能使无效区域有效,因此当程序跳出 WM_PAINT 时 ,无效区域仍然存在。系统就回不断发送WM_PAINT消息,于是程序不断处理WM_PAINT消息。

 

相当于BeginPaint、EndPaint会告诉GDI内部,这个窗口需要重画的地方已经重画了,这样WM_PAINT处理完返回给系统后,系统不会再重发WM_PAINT,而GetDC没有告诉系统这个窗口需要重画的地方已经画过,在你把程序返回给系统后,系统一直以为通知你的重画命令你还没有乖乖的执行或者执行出错,所以在消息空闲时,它还会不断地发WM_PAINT催促你画,导致程序卡死。

 

无效区域 :

无效区域就是指需要重画的区域,无效的意思是:当前内容是旧的,过时的。
假设A是新弹出的一个对话框或被激活的现有对话框,A对话框置于原来的活动对话框B前面,造成对话框B的部分或全部被覆盖,当对话框A移开或关闭后,使对话框B原来被覆盖的地方重新可见。那部分被覆盖的地方就称为无效区域。
只有当一个窗口消息空闲时,系统才会抽空检查一下这个窗口的无效区域是否为非空(WM_PAINT的优先级是最低的。这也就是为什么系统很忙时窗口和桌面往往会出现变白、刷新不了、留拖拽痕迹等现象的原因),如果非空,系统就发送WM_PAINT。所以一定要用BeginPaint、EndPaint把无效区域设为空,否则WM_PAINT将一直被发送。

 

来源:http://chat.begin09.com/viewthread.php?tid=4029

------------------------------------------------------------------------------------

 

在Window上主要有三种方法得到Client   Area的一个HDC句柄。
1.在WM_PAINT消息中调用   BeginPaint()方法,BeginPaint方法返回一个当前无效区域的HDC句柄,并把该无效区域设置为有效区域。所谓无效区域就是需要应用程序重新绘制的区域,反之则为有效区域。BeginPaint被调用时同时返回了一个PAINTSTRUCT结构,其中给出了本次重绘的Clip   Rectangle,所有在这个rectangle之外的绘制操作都不会显示。值得注意的是Petzold在砖头里面反复强调在WM_PAINT消息处理函数中BeginPaint方法需要和EndPaint方法配对使用,还有BeginPaint中返回的HDC不能缓存起来使用。
2.调用GetDC(hwnd)/ReleaseDC()来获取释放对应Client   Rectangle的HDC句柄,   这个句柄没有什么预定义的Clip   rectangle,   整个Client   Rectangle都可以绘制。但是GetDC()得到的句柄也同样不能缓存起来,也就是说GetDC和ReleaseDC函数要在一个消息处理函数中配对出现。
一个类似的API是GetWindowDC(),它用于获取整个Window的DC,(client   area   +   non-client   area)
和GetDC一样必须在一次WndProc执行中释放掉,不能缓存。

 

 

来源: http://blog.csdn.net/jiumingmao11982/article/details/2068445

------------------------------------------------------------------------------------

 

查了一些资料,说showwindows是显示窗口,UpdateWindow函数是更新窗口,一般的窗口创建过程大家都知道,如下:
1.设计窗口类
2.注册窗口类
3.ShowWindow()
4.UpdateWindow()
5.消息循环

问题较多,不过都挺经典。最近发现坛子里类似的问题挺多,所以在这里一并多罗嗦几句吧。

首先,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的区别了。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函数内部调用)。

 

来源: http://topic.csdn.net/u/20100818/23/b30e7e3d-2969-45ec-9054-1e98cca442e5.html
阅读更多

扫码向博主提问

shuilan0066

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • c++
  • mfc
  • python
  • duilib
去开通我的Chat快问
个人分类: 转载---图像处理
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭