Bootstrap

MFC的绘制问题

背景介绍

当时准备做一个读取配置文件的进度条,用以显示读取进度。进度条是用一个dialog配上Progressbar和Static控件做的,整体是通过MFC框架构建。读取数据期间通过不断向dialog发送消息来实现进度条的更新和文本变化。但是很奇怪的是,读到一定程度之后(比如20%)之后整个进度条不再更新进度,文本也不再发生变化.并且多次调试,发现每次都是从1%到20%左右正常,之后就完全不动了,真让人崩溃.

问题分析

我首先怀疑是由于界面太忙无法正常响应,经过多次调试我否定了这个想法,因为这里仅仅是读取文件,至多是IO,不涉及到绘制,并且打断点可以很明显的感受到整个程序完全正常,读取数据也没有卡顿.所以这里可以排除界面线程的问题.接着我怀疑是因为文件打开没关闭,因为读取的文件比较多,之前出现过文件未关闭,导致用户对象和句柄耗尽的问题,但是我认真审视了一遍代码,确信我在更新过程中未使用new来获取对象,那么是什么出现这个问题呢? 我甚至怀疑是windows的机制,只响应一定数量的SetWindowText,之后就不再响应了,于是我每读取一个文件强行UpdateWindow()一下,但是问题丝毫没有缓解,还是在20左右就不再更新了.真让人无语.

问题解决

实在没有办法了,我就把windows的资源管理器打开,并把所有的句柄,用户对象,GDI,CPU,内存等都调出来,等到软件调试时,观察这几项变化.这一观察就发现了问题.该进程的GDI在读取过程迅速从0升高到10000,在GDI达到10000后,界面上的进度条也跟着不走了.这就很明显了,这就说明在读取文件过程中在大量申请GDI对象,可是我还没有绘制,哪里会申请那么多GDI呢?

原因分析

后来在跟踪代码发现,每次读完数据之后我会创建一个对象,这个对象都会初始化一个通用对象.(整个系统架构是将所有对象放入doc类中,而通用方法(比如分割,数学运算等)放到一个通用类,而绘制画刷和画笔之类的本来在doc中,但是因为doc要存储各个绘制对象,各个绘制对象来调用doc的内容不方便,我将将这部分挪到通用类中了,并且其初始化放在了通用类的构造函数中),这样每new一个对象,就会向系统初始化一次这些画笔和画刷,这样没多久就将GDI给耗尽了,所以窗口想要更新界面时,已经无法从操作系统中获取GDI,导致后续的绘制失败,所以界面就一直保持静止的模样.

解决方案

知道了原因,就很方便解决了.我的解决方法是将这些画刷和画笔变为通用类的静态成员变量,这样每个实例初始化时,只会申请一次.说实话,画笔和画刷的静态对象初始化并不方便,费了好大力气,这里就不细说了.这篇主要的目的是记录一下调试过程,分享给遇到同样问题的小伙伴.希望一切顺利.

;