GDI对象增加错误:A required resource was unavailable

2024-03-27 11:48

本文主要是介绍GDI对象增加错误:A required resource was unavailable,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于应用程序出现窗口不完整,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);
     然而,在Windows 2000中,既便客户句柄的最大数目被硬设置为16384,那么为什么在实际中GDI对象增加到9999后,程序界面就开始混乱了呢?原来,在Windows2000中,每进程的GDI对象的最大值又被默认为10000——据微软资料显示,之所以设置为10000,是为了阻止“Bad”程序分配过多的资源,因此,当GDI对象达到9999之后,程序就不能再创建新的GDI资源,这样,每次都使用新建资源来绘制界面的程序就产生混乱了。
      不过,在Windows 2000及以后的操作系统中,每个进程可以创建的GDI对象的最大值,是可以通过修改注册表来重新设置的,在Windows 2000中,该注册表项所为:HKEY_LOCAL_MACHINE\ SOFTWARE\Microsoft\Windows NT\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对象数,其运行界面如下(这里图显示不出来,不过无关紧要)

     其中统计系统中总的GDI对象的代码如下:
int   GetGDINumInSystem(void)
{
     int nGDINums = 0;    /*所有进程的GDI对象之和*/
     int nProcess    = 0;    /*系统中的进程数*/

     DWORD aProID[1024];
     DWORD cbNeeded;
     ::EnumProcesses ( aProID, sizeof(aProID), &cbNeeded );

     /*系统中进程总数*/
     nProcess = cbNeeded / sizeof ( DWORD );

     /*统计每个进程的GDI对象数*/
     for ( INT i=0; i < nProcess; i ++ )
     {
         HANDLE hPro = ::OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,   FALSE,   aProID[i]   );

         nGDINums += ::GetGuiResources ( hPro, GR_GDIOBJECTS );

         CloseHandle ( hPro );
     }

     return nGDINums;
}

     使用此check程序,做如下试验:
     ①修改Windows 2000的注册表项” GDIProcessHandleQuota”值为12000;
     ②开启一个check进程,在其中创建11000个GDI对象!
     ③开启第二个check进程(第一个进程不关闭),在其中创建11000个GDI对象。
     试验结果表明,第一个进程的对象可以顺利创建,而第二个进程的对象则不能顺利创建,这就说明,在Windows 2000中,每个进程可以创建的GDI对象数,不仅在进程内部有一定限制,而且还受限于整个操作系统。经多次试验表明,当Windows 2000系统中总的GDI对象数达到15900以后的某个值后,进程就不能再创建GDI对象了,系统就变得不稳定了,至于该临界值到底是多少,各次试验结果并不一致,但是都在15900以后。

     上面的测试是在Windows 2000中进行的,那么,在Windows 2003中,情况又是什么样呢?立即动手,到2003系统中,修改注册表项”GDIProcessHandleQuota”为20000,然后运行测试程序并在其中创建20000个GDI对象,一切正常! 再改,30000,运行,仍然正常;……直到改到了70000的时候,系统运行才会出现界面绘制问题,在这样的事实下,不得不做假设:难道Windows 2003中取消了客户GDI句柄最多16K的限制,而将限制设到了64K?
     为了验证这个推测,我使用check程序直接创建64K的GDI资源,运行结果表明,当进程创建了一定的GDI对象之后,就不能创建新的GDI对象了,这表明上面的推测不完全正确,不过,基于在Windows 2000中的试验经验,很快就想到了在Windows 2003中系统可以创建的GDI对象应该还要受限于操作系统,也就是说:Windows 2003中放宽了每个进程可以创建的GDI对象数目,但是整个系统中GDI对象数不能超过某个值。为验证此结果,做如下试验:
    ①修改Windows 2003的注册表项” GDIProcessHandleQuota”值为50000;
    ②开启一个check进程,在其中创建40000个GDI对象!
    ③开启第二个check进程(第一个进程不关闭),在其中创建40000个GDI对象。
    试验结果基本上和Windows 2000中的结果类似,唯一不同的是,在第二个进程创建GDI对象的过程中,当系统中总GDI对象达到63700以后的某个值后才会创建失败。同样,该临界值也不固定,不过多次试验表明,当总的GDI对象数达到63700以后,系统就变得不稳定了。

结论
     经过上面的分析,我们可以知道,在Windows 2000/2003 操作系统中,每个进程可以创建的GDI对象,受限于三个方面因素:系统本身的两个方面的限制和Windows注册表中设置的最大值限制。
     首先,每个进程可以创建的GDI对象数,在理论上为64K,但是在Windows 2000中,系统将客户可以创建的GDI句柄数硬设置为不能超过16K,而事实上当GDI对象数达到12K之后,系统即不正常;在Windows 2003中,系统放宽了对GDI对象数的限制,使得每个进程可以使用的GDI对象数接近64K;
    
     其次,每个进程可以创建的GDI对象数,还受限于操作系统中GDI对象总数;在Windows 2000中,当系统中总的GDI对象达到15900以后的某个值后,进程就不能再创建新的GDI对象了;在20003中,这个值增加到63700以后的某个值。但是,到底是哪个值,则不固定。
    
     最后,进程可创建的最大GDI对象数目还受限于注册表中HKEY_LOCAL_MACHINE\ SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows下的"GDIProcessHandleQuota" 项所设置的值,该值设置了Windows中每个进程可以创建的最大GDI对象数,在Windows 2000及2003系统中,其默认均为10000,这也就说明了当程序创建的GDI对象数达到9999之后界面为什么会混乱。

相信,你看了之后,知道是怎么回事了吧。

总之一句话,创建了GDI/GDI+对象,用完之后,要释放!


转自 http://hi.baidu.com/qi_xian/blog/item/08011716e096751e962b4345.html

这篇关于GDI对象增加错误:A required resource was unavailable的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/852002

相关文章

Python错误AttributeError: 'NoneType' object has no attribute问题的彻底解决方法

《Python错误AttributeError:NoneTypeobjecthasnoattribute问题的彻底解决方法》在Python项目开发和调试过程中,经常会碰到这样一个异常信息... 目录问题背景与概述错误解读:AttributeError: 'NoneType' object has no at

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

Python struct.unpack() 用法及常见错误详解

《Pythonstruct.unpack()用法及常见错误详解》struct.unpack()是Python中用于将二进制数据(字节序列)解析为Python数据类型的函数,通常与struct.pa... 目录一、函数语法二、格式字符串详解三、使用示例示例 1:解析整数和浮点数示例 2:解析字符串示例 3:解