关于GDI资源使用后未释放,导致GDI对象猛增,程序花屏,异常退出的问题

来源:互联网 发布:春秋与战国的区别知乎 编辑:程序博客网 时间:2024/06/06 01:07

调试一个对话框程序,对话框上加载了自己实现的一个Grid控件。当添加多条数据后拖动滚动条多次后导致控件花屏。开始以为是ScrollWindow函数调用有问题,但打开资源管理器发现进程对应的GDI对象在刷新时猛增,到接近9999时便崩溃了,于是到重绘函数中发现GDI对象在调用后没有DeleteObject(),导致GDI对象一直增加。

        后来在网上查找到一片文章觉得不错,分享:

关于应用程序出现窗口不完整,GDI对象猛增,GDI资源泄漏的问题的探讨

 

有时候,一个应用程序运行到一定的时间,会出现窗口不完整(花屏),出现“必需的资源无法得到”的报错,这是个令人烦恼的问题。此时,你如果打开资源管理器,在“查看”中“选择列”,添加“GDI对象”,可以很清晰得看到,随着程序的运行,GDI对象,快速地增加,当数量达到9999时(为什么是这个数,下文会提到)时,程序窗口界面就会出现不完整现象,此时,你若拖动程序里的滚动条之类的,将会出现严重的花屏,甚至还会弹出一个不完整的警告框,警告:“必需的资源无法得到”。

这是典型的GDI资源泄漏的问题。

之所以会出现这样的问题,主要是你在程序中创建了GDI对象,之后并没有释放或消毁等等。这种问题一般出现在OnPaint(),Draw()还有一些涉及到绘图的函数中。下面举例说明:

1、一个新的GDI对象选择到了DC,但使用完后没有恢复DC中的原始GDI对象。

CGDIObject*pOldDC=pDC->SelectObject(&yourobject);

//...... 

pDC->SelectObject(pOldDC);//在绘图结束时,这句不能少

2、通过GetDC()等获得的上下文CDC,使用后,必须Realease。

3、创建 BITMAP 对象,最后要DeleteObject()。

4、这是我遇到的问题:在做文件搜索时,用到CListCtrl显示搜索结果,我想在文件名前显示文件图标,因此用函数:

  ::SHGetFileInfo (pathname,FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO),SHGFI_USEFILEATTRIBUTES |SHGFI_DISPLAYNAME |SHGFI_TYPENAME|SHGFI_ICON|SHGFI_SMALLICON))  得到文件的图标,但后来我并没有用到,因此就放那不管了,由此“铸成大错”,后果不堪设想,如上文所描述的那样。解决方法是去掉红色部分就可以了。这时,运行程序,GDI对象的数量一般稳定在32-35之间,不会出现一瞬间就飙升到9999的可怕景象了。

下面说说GDI对象问题。

为解决我遇到的问题,我上网寻觅许久,最后看到henan_lujun(网名,引自:http://www.programfan.com/club/showpost.asp?id=7995)的分析,明白些许,下面转载片段:

    GDI对象,实际上是Windows系统维护的一些数据结构。微软基于稳定性和健壮性考虑,将所有GDI对象的管理权都交给Windows系统的对象管理器管理,用户只能通过系统返回的“句柄”来操作这些对象。

     在Windows 2000中,句柄实际上是一个DWORD类型的值。该DWORD值是一个32比特位的数据,它又分为两个部分:Table Index及Uniqueness Identifier,他们各占16位,因此,在理论上来说,Windows中的每个进程,所能访问的GDI对象的最大值是64K。然而,在 Windows 2000中,客户句柄的最大数目被硬设置为16384 (16K);

     然而,在Windows2000中,既便客户句柄的最大数目被硬设置为16384,那么为什么在实际中GDI对象增加到9999后,程序界面就开始混乱了呢?原来,在 Windows2000中,每进程的GDI对象的最大值又被默认为10000——据微软资料显示,之所以设置为10000,是为了阻止“Bad”程序分配过多的资源,因此,当GDI对象达到9999之后,程序就不能再创建新的GDI资源,这样,每次都使用新建资源来绘制界面的程序就产生混乱了。

     不过,在Windows 2000及以后的操作系统中,每个进程可以创建的GDI对象的最大值,是可以通过修改注册表来重新设置的,在Windows 2000中,该注册表项所为:HKEY_LOCAL_MACHINE\ SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Windows下的"GDIProcessHandleQuota"。设置完新的值后,重启计算机后,系统中每进程 可以使用的GDI对象数就会变成你新设置的数。

然而,上述说法并不完全正确!

     据SYBASE的一份资料显示(http://www.sybase.com/detail?id=1019174),在Windwos2000中,只可 以对”GDIProcessHandleQuota”值进行微调,如果设置的值超过15000,系统就变得不稳定。事实上,我在Windows 2000中进行了测试,当GetGuiResources函数的返回值为12288(12K)时,就已经不能创建新的GDI对象了,也就是说,在 Windows 2000中当一个进程总的GDI对象数达到12288以后,就不能再创建新的GDI对象了。

     那么,在Windows 2000中,进程所能创建的GDI对象数,是每个进程独立的,还是要受限于Windows操作系统呢?为此,我写了一个check程序,该程序批量创建指 定数目的BRUSH对象,并统计本进程的GDI对象数及系统中总的GDI对象数。


0 0
原创粉丝点击