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

相关文章

EMLOG程序单页友链和标签增加美化

单页友联效果图: 标签页面效果图: 源码介绍 EMLOG单页友情链接和TAG标签,友链单页文件代码main{width: 58%;是设置宽度 自己把设置成与您的网站宽度一样,如果自适应就填写100%,TAG文件不用修改 安装方法:把Links.php和tag.php上传到网站根目录即可,访问 域名/Links.php、域名/tag.php 所有模板适用,代码就不粘贴出来,已经打

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

【经验交流】修复系统事件查看器启动不能时出现的4201错误

方法1,取得『%SystemRoot%\LogFiles』文件夹和『%SystemRoot%\System32\wbem』文件夹的权限(包括这两个文件夹的所有子文件夹的权限),简单点说,就是使你当前的帐户拥有这两个文件夹以及它们的子文件夹的绝对控制权限。这是最简单的方法,不少老外说,这样一弄,倒是解决了问题。不过对我的系统,没用; 方法2,以不带网络的安全模式启动,运行命令行,输入“ne

HTML5自定义属性对象Dataset

原文转自HTML5自定义属性对象Dataset简介 一、html5 自定义属性介绍 之前翻译的“你必须知道的28个HTML5特征、窍门和技术”一文中对于HTML5中自定义合法属性data-已经做过些介绍,就是在HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们要在一个文字按钮上存放相对应的id: <a href="javascript:" d

PHP7扩展开发之对象方式使用lib库

前言 上一篇文章,我们使用的是函数方式调用lib库。这篇文章我们将使用对象的方式调用lib库。调用代码如下: <?php $hello = new hello(); $result = $hello->get(); var_dump($result); ?> 我们将在扩展中实现hello类。hello类中将依赖lib库。 代码 基础代码 这个扩展,我们将在say扩展上增加相关代码。sa

hibernate修改数据库已有的对象【简化操作】

陈科肇 直接上代码: /*** 更新新的数据并并未修改旧的数据* @param oldEntity 数据库存在的实体* @param newEntity 更改后的实体* @throws IllegalAccessException * @throws IllegalArgumentException */public void updateNew(T oldEntity,T newEntity

SQL2005 性能监视器计数器错误解决方法

【系统环境】 windows 2003 +sql2005 【问题状况】 用户在不正当删除SQL2005后会造成SQL2005 性能监视器计数器错误,如下图 【解决办法】 1、在 “开始” --> “运行”中输入 regedit,开启注册表编辑器,定位到 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVer

类和对象的定义和调用演示(C++)

我习惯把类的定义放在头文件中 Student.h #define _CRT_SECURE_NO_WARNINGS#include <string>using namespace std;class student{public:char m_name[25];int m_age;int m_score;char* get_name(){return m_name;}int set_name

react笔记 8-19 事件对象、获取dom元素、双向绑定

1、事件对象event 通过事件的event对象获取它的dom元素 run=(event)=>{event.target.style="background:yellowgreen" //event的父级为他本身event.target.getAttribute("aid") //这样便获取到了它的自定义属性aid}render() {return (<div><h2>{

一些数学经验总结——关于将原一元二次函数增加一些限制条件后最优结果的对比(主要针对公平关切相关的建模)

1.没有分段的情况 原函数为一元二次凹函数(开口向下),如下: 因为要使得其存在正解,必须满足,那么。 上述函数的最优结果为:,。 对应的mathematica代码如下: Clear["Global`*"]f0[x_, a_, b_, c_, d_] := (a*x - b)*(d - c*x);(*(b c+a d)/(2 a c)*)Maximize[{f0[x, a, b,