您的位置:首页 > 大数据 > 人工智能

在消息响应函数中立即处理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等几个方面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐