创建新窗口时ShowWindow和UpdateWindow的一些疑问。。

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

窗口过程函数里的WM_paint消息就简单地输出一个"test"字符串如下:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,0,0,L"test",wcslen(L"test"));
EndPaint(hWnd, &ps);
break;


我想问的是:
(1)在ShowWindow()后,为何还要多用一个UpdateWindow(),我在实质测试时把UpdateWindow()删除时运行结果还是一致的。
(2)据查资料,UpdateWindow()函数实质是产生一个WM_PAINT消息,所以在运行程序后,弹出的窗口中就输出了test字符,这个可以理解,但为何把UpdateWindow()函数注释掉,只保留ShowWindow(),程序运行时还是会出现test字符?如果认为是窗口从无到有时产生重绘消息产生的文本,那是否表示在不注释掉UpdateWindow()时已经响应了两次WM_PAINT消息呢?
(3)关于程序运行顺序的问题,按照正常的运行步骤应该是:ShowWindow和UpdateWindow后,进行消息循环里的
while (GetMessage(&msg, NULL, 0, 0))

从而将消息取出进行响应,但实质调试时,在UpdateWindow()、while (GetMessage(&msg, NULL, 0, 0))和case WM_PAINT三处分别设置断点,在调试时,发现实质的运行顺序是UpdateWindow()后直接到case WM_PAINT,进行了消息响应,再到消息循环的while (GetMessage(&msg, NULL, 0, 0)),也就是说UpdateWindow产生WM_PAINT消息后直接就响应了消息,而不是通过这GetMessage()取出再响应的,这点让我很不解,求指教

问题比较繁琐且啰嗦,菜鸟一个,求指点。。。
...全文
1277 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
bigfat123456 2012-08-01
  • 打赏
  • 举报
回复
太好了
手抓饼加辣 2012-07-17
  • 打赏
  • 举报
回复
讲的很好,为初学者解惑太实用了。
yapai 2012-02-08
  • 打赏
  • 举报
回复
讲的很好,感谢
codesnail 2010-11-26
  • 打赏
  • 举报
回复
讲的太好了。
stonewf 2010-10-30
  • 打赏
  • 举报
回复
刚好用到~~
yang_jnu 2010-08-19
  • 打赏
  • 举报
回复
哦,明白。。。非常感谢指点。。。。
wltg2001 2010-08-19
  • 打赏
  • 举报
回复
显示/隐藏窗口只能用ShowWindow
其中的隐藏窗口是指最小化,还是在别的窗口之下?
=============
隐藏就是隐藏,也就是看不见了,既不是最小化,也不是在别的窗口之下,比如你调用:
ShowWindow(hWnd,SW_HIDE);之后,这个窗口就看不见了。
yang_jnu 2010-08-19
  • 打赏
  • 举报
回复
非常感谢楼上的高手解释,解释的非常清楚。。有些概念性的问题想问下,您说的:
显示/隐藏窗口只能用ShowWindow
其中的隐藏窗口是指最小化,还是在别的窗口之下?
yang_jnu 2010-08-19
  • 打赏
  • 举报
回复
天呀,好详细,太感谢了。。可惜我已经结贴了,真不好意思,加不了分了。
xxd_qd 2010-08-19
  • 打赏
  • 举报
回复
问题较多,不过都挺经典。最近发现坛子里类似的问题挺多,所以在这里一并多罗嗦几句吧。

首先,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函数内部调用)。
wltg2001 2010-08-18
  • 打赏
  • 举报
回复
关于上面的第三个问题,其实在MSDN中已经有说明了:
The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT message to the window if the window's update region is not empty. The function sends a WM_PAINT message directly to the window procedure of the specified window,
wltg2001 2010-08-18
  • 打赏
  • 举报
回复
(1)在ShowWindow()后,为何还要多用一个UpdateWindow(),我在实质测试时把UpdateWindow()删除时运行结果还是一致的。
(2)据查资料,UpdateWindow()函数实质是产生一个WM_PAINT消息,所以在运行程序后,弹出的窗口中就输出了test字符,这个可以理解,但为何把UpdateWindow()函数注释掉,只保留ShowWindow(),程序运行时还是会出现test字符?如果认为是窗口从无到有时产生重绘消息产生的文本,那是否表示在不注释掉UpdateWindow()时已经响应了两次WM_PAINT消息呢?
====================
WM_PAINT消息是否产生关键是看是不是有无效区出现。关于ShowWindow与UpdateWindow的作用,ShowWindow的作用是显示/隐藏窗口,UpdateWindow刷新窗口,显示/隐藏窗口只能用ShowWindow,当然显示时也会刷新。所以刷新窗口 这两个都可以用。

如果认为是窗口从无到有时产生重绘消息产生的文本,那是否表示在不注释掉UpdateWindow()时已经响应了两次WM_PAINT消息呢?
=============
任何情况下消息队列中都不可能出现两个WM_PAINT消息的,当有两个WM_PAINT消息,系统会将两个无效区合并成一个,形成一个WM_PAINT消息。

(3)关于程序运行顺序的问题,按照正常的运行步骤应该是:ShowWindow和UpdateWindow后,进行消息循环里的
C/C++ codewhile (GetMessage(&msg, NULL, 0, 0))

从而将消息取出进行响应,但实质调试时,在UpdateWindow()、while (GetMessage(&msg, NULL, 0, 0))和case WM_PAINT三处分别设置断点,在调试时,发现实质的运行顺序是UpdateWindow()后直接到case WM_PAINT,进行了消息响应,再到消息循环的while (GetMessage(&msg, NULL, 0, 0)),也就是说UpdateWindow产生WM_PAINT消息后直接就响应了消息,而不是通过这GetMessage()取出再响应的,这点让我很不解,求指教
=======================
这个问题就涉及到UpdateWindow了,本来WM_PAINT消息的优先级是很低的,窗口不一定会及时处理这个消息,而UpdateWindow会让窗口及时处理WM_PAINT消息。

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧