在消息响应函数中立即处理PENDING WM_PAINT解决刷新问题
2008-12-25 11:14
681 查看
一、问题的提出
在某个IM的开发中,我们一直被一个性能问题困扰,由于聊天窗口关闭时需要做许多额外工作,诸如清除数据、反初始化控件、通过SDK通知插件……在这些额外工作中,通知插件是一个同步的操作,目前需要占用较可观的处理时间。于是,这些额外工作导致了关闭聊天窗口时性能低下,窗口不能立即被关闭,在屏幕上迟迟无法消失,严重影响了用户的体验!
二、一个蹩足的解决方案
既然无法提高关闭聊天窗口的速度,自然,我们想到了一个看似能解决问题的方案——何不在真正关闭聊天窗口前先HIDE一下它,先让它从屏幕上消失,给用户一个错觉,我们的聊天窗口再在HIDE状态慢慢地打扫干净。
我们在关闭时先HIDE窗口,处理一些必要的退出逻辑,再设置一个定时器,在定时器事件中才真正销毁窗口。
看起来一切正常,事实上,在前期,通过SDK给插件发消息的处理速度还未变得像现在这样慢的时候,是有效果的!随着开发的推进,这一方案越来越暴露出它的不足,到现在,不幸的是,基本上,它不工作了!我们关闭聊天窗口,虽然执行了HIDE,但它并不立即消失。当把聊天窗口放到主窗口上时,聊天窗口关闭时的残留图像变得更加明显!
三、正确的分析
这个问题暴露出来以后,我对关闭时先HIDE这种方案进行了认真分析,原来,虽然HIDE了聊天窗口,但是在响应WM_PAINT消息前,屏幕上聊天窗口的图像还在的。只有等到WM_PAINT响应,聊天窗口的图像才会被清除。
为什么没有立即响应WM_PAINT消息呢?因为我们是在窗口的消息处理函数中(WM_CLOSE消息的响应中)HIDE窗口的。消息循环正被我们占用着,还没有返回,它没有机会去响应WM_PAINT消息——尽管这个消息已经被Windows放到了消息队列中。
这样看来,HIDE窗口操作完全是无效的!
四、最佳的解决方案
怎么才能让HIDE窗口之后立即生效呢?
前几天,我业余时间刚好研究了MFC文档视图类应用程序的换肤问题,在学习MFC中的CDockContext的代码时,看到MFC为了让拖拽工具栏时主窗口原工具栏位置能立即更新,它使用了一个很好的方法:
// handle pending WM_PAINT messages
MSG msg;
while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_NOREMOVE))
{
if (!GetMessage(&msg, NULL, WM_PAINT, WM_PAINT))
return;
DispatchMessage(&msg);
}
因为拖拽操作是在WM_LBUTTONDOWN中进行的,在消息响应函数中虽然把ToolBar拖走了,但由于WM_PAINT还未调用,所以主窗口不会立即刷新。上面的代码巧妙地让WM_PAINT得到立即执行,主窗口立即重绘了变化的区域。
这真是太好了,这个IM聊天窗口遇到了同一问题!
于是,我在HIDE聊天窗口后增加上面的几行代码,让未决的WM_PAINT消息立即执行,奇迹出现了,聊天窗口在主窗口上不再有残留的图像,看起来是立即关闭的一样!
五、小结
其实,用这个方法来立即响应WM_PAINT消息后,不再需要定时器的!去掉定时器,代码的逻辑可以简化,避免定时器导致的问题(前面,我和不凡遇到过定时器放置的位置小小挪动一下导致插件处理消息时的异常)。
这个方法我们可以用到一切需要立即刷新界面的地方!当我们在消息响应函数中需要立即刷新界面时,尽管窗口的消息循环被占用了,但我们可以向MFC的代码那样,用PeekMessage、GetMessage、DispatchMessage自己再搞一个临时的消息循环,让未处理的WM_PAINT消息被立即处理掉。
至于这个方法可能引起的问题,比如函数重入问题,还不太清楚,需要继续研究。这里的简单使用应该没有问题。我搜索MFC的代码,这种方法有不少的应用,主要用在了拖拽、窗体上下文帮助(ContextHelp)、Socket等几个方面。
在某个IM的开发中,我们一直被一个性能问题困扰,由于聊天窗口关闭时需要做许多额外工作,诸如清除数据、反初始化控件、通过SDK通知插件……在这些额外工作中,通知插件是一个同步的操作,目前需要占用较可观的处理时间。于是,这些额外工作导致了关闭聊天窗口时性能低下,窗口不能立即被关闭,在屏幕上迟迟无法消失,严重影响了用户的体验!
二、一个蹩足的解决方案
既然无法提高关闭聊天窗口的速度,自然,我们想到了一个看似能解决问题的方案——何不在真正关闭聊天窗口前先HIDE一下它,先让它从屏幕上消失,给用户一个错觉,我们的聊天窗口再在HIDE状态慢慢地打扫干净。
我们在关闭时先HIDE窗口,处理一些必要的退出逻辑,再设置一个定时器,在定时器事件中才真正销毁窗口。
看起来一切正常,事实上,在前期,通过SDK给插件发消息的处理速度还未变得像现在这样慢的时候,是有效果的!随着开发的推进,这一方案越来越暴露出它的不足,到现在,不幸的是,基本上,它不工作了!我们关闭聊天窗口,虽然执行了HIDE,但它并不立即消失。当把聊天窗口放到主窗口上时,聊天窗口关闭时的残留图像变得更加明显!
三、正确的分析
这个问题暴露出来以后,我对关闭时先HIDE这种方案进行了认真分析,原来,虽然HIDE了聊天窗口,但是在响应WM_PAINT消息前,屏幕上聊天窗口的图像还在的。只有等到WM_PAINT响应,聊天窗口的图像才会被清除。
为什么没有立即响应WM_PAINT消息呢?因为我们是在窗口的消息处理函数中(WM_CLOSE消息的响应中)HIDE窗口的。消息循环正被我们占用着,还没有返回,它没有机会去响应WM_PAINT消息——尽管这个消息已经被Windows放到了消息队列中。
这样看来,HIDE窗口操作完全是无效的!
四、最佳的解决方案
怎么才能让HIDE窗口之后立即生效呢?
前几天,我业余时间刚好研究了MFC文档视图类应用程序的换肤问题,在学习MFC中的CDockContext的代码时,看到MFC为了让拖拽工具栏时主窗口原工具栏位置能立即更新,它使用了一个很好的方法:
// handle pending WM_PAINT messages
MSG msg;
while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_NOREMOVE))
{
if (!GetMessage(&msg, NULL, WM_PAINT, WM_PAINT))
return;
DispatchMessage(&msg);
}
因为拖拽操作是在WM_LBUTTONDOWN中进行的,在消息响应函数中虽然把ToolBar拖走了,但由于WM_PAINT还未调用,所以主窗口不会立即刷新。上面的代码巧妙地让WM_PAINT得到立即执行,主窗口立即重绘了变化的区域。
这真是太好了,这个IM聊天窗口遇到了同一问题!
于是,我在HIDE聊天窗口后增加上面的几行代码,让未决的WM_PAINT消息立即执行,奇迹出现了,聊天窗口在主窗口上不再有残留的图像,看起来是立即关闭的一样!
五、小结
其实,用这个方法来立即响应WM_PAINT消息后,不再需要定时器的!去掉定时器,代码的逻辑可以简化,避免定时器导致的问题(前面,我和不凡遇到过定时器放置的位置小小挪动一下导致插件处理消息时的异常)。
这个方法我们可以用到一切需要立即刷新界面的地方!当我们在消息响应函数中需要立即刷新界面时,尽管窗口的消息循环被占用了,但我们可以向MFC的代码那样,用PeekMessage、GetMessage、DispatchMessage自己再搞一个临时的消息循环,让未处理的WM_PAINT消息被立即处理掉。
至于这个方法可能引起的问题,比如函数重入问题,还不太清楚,需要继续研究。这里的简单使用应该没有问题。我搜索MFC的代码,这种方法有不少的应用,主要用在了拖拽、窗体上下文帮助(ContextHelp)、Socket等几个方面。
相关文章推荐
- 关于在窗口消息处理函数中使用MessageBox造成消息重入的问题的研究及解决
- 解决在epoll中accept接收端口会漏处理的问题. 直到新的socket消息到达,epoll_wait才响应去接收socket端口数据的问题
- 处理 C++ 项目中的 IntelliSense 失败:输入类实例后,不能显示其成员几成员函数问题解决
- WM_CREATE和WM_INITDIALOG消息响应函数之区别,以及MFC应用程序中处理消息的...
- VC编程Windows消息处理机制、阻塞试验、SetTimer、MessageBox、小心消息响应处理函数
- MFC与SDK中的重画问题 。。WM_PAINT消息的处理
- 一个解决OnNcLButtonUp不能响应消息问题的更好办法
- VirtualBox的Ubuntu中文件共享问题的解决:未处理的错误消息,获取文件"/media/sf_***"信息出错,协议错误
- 解决vue this.$forceUpdate() 处理页面刷新问题(v-for循环值刷新等)
- 解决MATLAB 图像处理中,blkproc 函数报错的问题
- 解决给MFC的类添加消息响应函数时报错“because the code element 'Cxxx' is read only ”
- 【VS2010学习笔记】【函数学习】一(MFC+OpenCV2.4.7读取摄像头之WM_TIMER消息处理函数的添加问题)
- 自编函数解决pathinfo()函数处理中文问题
- "响应消息的内容类型 text/html; charset=utf-8 与绑定(text/xml; charset=utf-8)的内容类型不匹配。"问题的解决办法
- 双击消息(OnNMDblclkList)处理函数中不能设断点的问题
- 解决金山急救箱在安全模式下无法显示“立即处理”按钮的问题
- lamp使用php处理上传文件,调用move_uploaded_file函数遇到目录写权限问题及解决过程
- 键盘消息处理(按下某个键,响应一个函数)
- asp.net c#的传参中经常使用到中文参数的处理,下面的函数可以解决中文参数的问题
- MFC单文档程序添加消息映射和消息响应函数的问题