MFC工程的Dui+Win32改造案例遇到的问题总结

1        案例描述

##5.0(包含)之前的的截图库,是基于MFC框架实现的。鉴于##现在的主工程和其他所有相关工程,已经全部Dui化,为了和主工程保持一致,资源图标等统一管理,也不再引用MFC库,遂决定,将截图库也Dui化实现。使用Dui化实现后,截图库大小从3MB左右减少到100多KB,缩小了安装包的大小。

而本案例,就是在改造**中,MFC版本的截图库Dui化+Win32时遇到的一系列问题,及解决这些问题的方法、思路,供后来者参考。有利于加深对Win32编程的理解,分享在Win32编程过程中遇到问题及其解决过程和方法。

2        案例分析与问题解决

下面针对遇到的一些主要问题及其解决方法列举如下:

一、          WM_PAINT过多的问题

1、WM_PAINT消息过多,导致低优先级的WM_TIMER消息收不到的问题

2、WM_PAINT消息过多,导致矩形、椭圆等图元实时绘制刷新闪烁的问题

在OnPaint函数中,用BeginPaint返回的dc将GetDC获取的dc替换掉,就可以解决这两个问题。

下面就对BeginPaint和EndPaint与GetDC()和ReleaseDC()的区别进行分析说明:

1. BeginPaint()和EndPaint()

在WM_PAINT消息里面绘图, 一般用BeginPaint() 与EndPaint()。BeginPaint()自动设置裁剪区域,把不更新的区域设置到更新区域外面,此消息只绘制无效区域,更新区域可以显示地被InvalidateRect 和InvalidateRgn设置,如果背景要擦除,BeginPaint发送消息 WM_ERASEBKGND 给窗口。BeginPaint 还会自动地把无效区域设置为有效区域,BeginPaint() 和EndPaint() 可以删除消息队列中的WM_PAINT消息,使系统不会重复发送大量的WM_PAINT消息,这样系统就有空闲处理比WM_PAINT消息优先级更低的消息,如WM_TIMER消息。但是,BeginPaint和EndPaint 只能在WM_PAINT消息里面进行调用 ,两者一定要成对出现。

2.GetDC()和ReleaseDC()

GetDC 可以用在任何需要DC的地方,它不会删除WM_PAINT消息,也不能使无效区域有效,当程序跳出 WM_PAINT 时,无效区域仍然存在,所以系统会不停的发送消息要求无效区域自绘。系统就会不断发送WM_PAINT消息,于是程序不断处理WM_PAINT消息,从而导致闪烁,也导致窗口过程收不到比WM_PAINT消息优先级低的其他消息。这也是引起该的根本原因。

下面是测试绘制父窗口和子控件的使用双缓冲绘图,因为不当的使用GetDC出现的问题,而使用CPaintDC却没有这个问题,CPaintDC类内部是使用BeginPaint构造的。

在MFC测试工程TestWndDraw中,测试绘制对话框背景时的子控件的显示问题。在MFC对话框的资源中添加如下的3个子控件,如图1所示。

MFC工程的Dui+Win32改造案例遇到的问题总结_第1张图片

图 1

在void CTestWndDrawDlg::OnPaint()中添加的刷新代码,如图2所示。

                     MFC工程的Dui+Win32改造案例遇到的问题总结_第2张图片

图 2

直接使用GetDC进行窗口绘制,窗口中的子控件不显示,如图3所示,设置clipchildren属性(WS_CLIPCHILDREN)则可以显示出来,但是如果最小化后还原,则会将对话框窗口后面的窗口中内容clip出来,子控件中显示异常。

MFC工程的Dui+Win32改造案例遇到的问题总结_第3张图片

图 3

如果移动一下对话框,子控件又正常了,设置WS_CLIPCHILDREN窗口风格,是不能解决根本问题,而且会带来新的问题。在OnPaint函数中,用BeginPaint返回的dc将GetDC获取的dc替换掉,就可以解决这两个问题。

 

二、          EditWnd框,虚线框刷新的问题

添加文字编辑框,显示效果是文字距离边距特别近,移动刷新时还不断闪烁。在截图窗口有WS_CLIPSIBLINGS和WS_CLIPCHILDREN风格时,QQ输入法输入汉字时编辑框没有泛白现象,而MFC版本截图是没有的,参考MFC版本是直接使用WM_ERASEBKGND传递过来的CDC对象,如图4所示。

MFC工程的Dui+Win32改造案例遇到的问题总结_第4张图片

图 4

查阅MSDN的关于WM_ERASEBKGND的说明,可以使用消息传递过来的dc句柄来绘制,如图5所示。

MFC工程的Dui+Win32改造案例遇到的问题总结_第5张图片

图 5

WM_ERASEBKGND消息传递过来的dc句柄与GetDC获取的dc是有区别的。将编辑框的虚线框绘制在编辑框内部,移动是没有问题的,移动过程中一直都有虚线框,但是将虚线框绘制到外部,拖动编辑框过程中虚线框被刷没了,对比MFC版本的截图,有两处不同:

1、MFC版本截图主对话框是没有WS_CLIPSIBLINGS和WS_CLIPCHILDREN风格

2、在MFC版本截图主对话框的OnPaint函数中,使用的是CPaintDC,CPaintDC内部实现是使用BeginPaint

解决办法:

1、去掉截图主窗口的WS_CLIPSIBLINGS和WS_CLIPCHILDREN风格

2、在OnPaint函数中,用BeginPaint返回的dc将GetDC获取的dc替换掉

三、          截图窗口不能全屏的问题

截图窗口已经根据系统的分辨率,放大窗口到全屏,结果还是无法覆盖到系统的任务栏。而MFC版本的就没有这个问题。经SPY++工具查看窗口样式,发现Dui默认的窗口样式中有WS_THICKFRAME样式,去掉这个窗口样式后,即可以实现全屏截图。

四、          快速操作,会出现亮色图区域和橡皮筋类的区域不一致的问题

MFC工程的Dui+Win32改造案例遇到的问题总结_第6张图片

图 6

关于快速操作,会出现亮色图区域和橡皮筋类的区域不一致的问题,猜测是鼠标弹起时,在响应鼠标右键弹起时鼠标的真实坐标和函数响应后实时获得的鼠标坐标不一致导致的。查看MSDN,发现鼠标消息中的LPARAM参数,携带有鼠标的位置,拆解提取鼠标位置坐标后,此问题解决。

 

五、          GDI资源泄露问题

截图前,使用GDIView.exe查看,找到**的进程,选中并查看,如图7所示,当前进程中的不同的额GDI对象的数量,可先截图当前界面保存,在使用完**截图功能后,刷新查看,如图8所示。再次查看**的对应的GDI资源,发现有些资源没有释放。比如,Pen资源,若在使用**截图后的个数比使用截图前的个数大,就说明,截图过程中Pen资源没有释放,可以有针对性的查找Pen资源是否没有释放造成GDI对象泄露,其他资源类似比较处理。

MFC工程的Dui+Win32改造案例遇到的问题总结_第7张图片

图 7


图 8

因此,在win32中使用GDI资源,注意使用完就释放,不能泄露。而MFC版本的GDI资源都是封装成类,都是在对应类的析构函数中释放的。

3        解决结果

经过不断测试发现,在响应WM_PAINT消息处,使用BeginPaint和EndPaint而不是GetDC()和ReleaseDC(),注重区分,问题就会减少很多。现在使用Dui改造后的截图和之前MFC版本的截图功能、效果上都一样了,改造完成。

4        总结

实际改造的效果和预想的效果可能出现偏差,多使用SPY++、MSDN或其他相关工具,如GDIView.exe等,比较Dui窗口样式和之前的MFC版本的窗口样式的区别,善于借用工具和查看MSDN对一些API和对应Windows消息的解释和说明。注重区分BeginPaint()和EndPaint()以及GetDC()和ReleaseDC()的不同使用场景。

你可能感兴趣的:(VC++)