ArcEngine中Com对象释放的技巧

声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
欢迎加群: GIS开发部落

缘起

AE开发中经常会和Com对象打交道,合理的释放Com对象才能使程序运行的稳定快速。Com对象不及时释放可能会引发以下问题

  • 内存占用高,极端情况引发内存异常
  • 程序异常
  • 数据锁定,如表锁定、镶嵌数据集锁定、文件锁定等问题
  • 无法删除文件(如shapefile)
  • 无法删除FeatureClass,无法修改表结构

本文主要总结如何释放Com对象,以及释放Com对象的技巧,学会如何释放Com对象是一道必备的技能,总结一下,For Me And For You,希望你也能写出不令人唾弃的代码。

常见要释放的Com对象

游标对象(如IFeautreCursor、ICursor)、Geodatabase对象(如IWorkspace、IDataset、IFeatureClass、ITable、IRaster、IRasterDataset、IMosaciDataset等)、StyleGallery对象等。

Com对象释放的几种方法

ArcEngine帮助参考位置
在这里插入图片描述

AOUninitialize.Shutdown

在这里插入图片描述
有时候你开发的独立的程序关闭的时候会报一个意想不到的错误,当你退出一个加载了MapControl的ArcEngine程序时你也许会收到类似如下的错误,The instruction x references memory at x. The memory could not be read.
当COM对象在内存中保留的时间超过预期时,可能会发生这些错误,从而阻止COM库在进程关闭时从进程中正确卸载。为了帮助防止这些错误,添加ESRI.ArcGIS.ADF.Local引用,添加一个静态的Shutdown函数,此函数通过确保在进程关闭之前卸载未使用的COM引用,有助于避免这些错误。

ESRI.ArcGIS.ADF.COMSupport.AOUninitialize.Shutdown()

此函数只帮助卸载没有COM对象的库。在处理完任何COM对象后应用此函数更为有益。例如,在带有启动窗体的Arcgis Engine for Windows应用程序中,将对aoUninitialize.shutdown的调用放在窗体释放的事件处理程序中。

ComReleaser

使用前添加ESRI.ArcGIS.ADF.Connection.Local.dll ,常见 comReleaser.ManageLifetime方法管理Com对象,如下是简单的示例代码:

using (ComReleaser comReleaser = new ComReleaser())
{
    var comObj1=......;//伪代码
    comReleaser.ManageLifetime(comObj1);
    ......
    var comObj2=......;//伪代码
    comReleaser.ManageLifetime(comObj2);
    ......
}

使用ComReleaser管理Com对象,当Using语句执行完毕,所有的Com对象即会被释放,建议Com对象管理的代码和对象放到一起,这样无论是修改代码还是检查代码都比较简单。

Marshal.ReleaseComObject

需要在代码文件的开头添加类型引用 Using System.Runtime.InteropServices.Marshal ,一般使用 Marshal.ReleaseComObject方法释放Com对象,使用示例如下:

private void MyFunction()
{    
	ESRI.ArcGIS.Display.IStyleGallery styCls = new	ESRI.ArcGIS.Framework.StyleGalleryClass()as	ESRI.ArcGIS.Display.IStyleGallery;    // Use the StyleGalleryClass here.
    int refsLeft = 0;
    do
	{
        refsLeft = Marshal.ReleaseComObject(styCls);
	}
    while (refsLeft > 0);
}

Com对象释放注意事项

  1. 优先使用ComReleaser管理Com对象
  2. 在使用Com对象的时候,最好就管理起来,避免遗漏
  3. 循环中产生的Com对象,建议及时回收,不要等循环完毕一次性回收
  4. 函数生成的Com对象也要释放,避免遗漏(如使用ISaveAs接口保存图片的时候)

Com对象释放常见错误

以下是我项目实际开发中遇到的各种坑爹代码,建议阅读以下,避免写出惨绝人寰的代码。(Ps:以下示例代码均为伪代码)

  • 释放的目标对象选择错误,比如对Null对象释放

    Using(ComReleaser = new ComReleaser())
    {
    	IFeatureCursor pFeaCursor=pFeatureClass.Search(null,true);
    	//错误写法
    	IFeature pFeature = null;
    	pComReleaser.ManageLifetime(pFeature);//类似这种的代码,对Null对象上管理,基本无用
    	pFeature=pFeaCursor.NextFeature();
    	//正确写法
    	IFeature pFeature=pFeaCursor.NextFeature();
    	if(pFeature!=null)
    	{
    		pComReleaser.ManageLifetime(pFeature);//要管理非空的对象才有意义
    		......
    	}	
    }
    
  • 对同一个对象或全局对象多次释放(这种问题一般在IWorkspaceFactory IWorkspace IFeatureClass上比较常见,建议大家了解一下单例模式 和 ArcEngine中资源池的概念)

    //定义一个IWorkspace对象(可以是全局对象或者单例对象,不懂单例的可以百度单例模式)
    IWorkspace pWs = pWsFactory.OpenWorkspace(wsPath,0);
    //用一个Workspace同时打开两次要素类,得到的其实是同一个要素类引用
    IFeatureClass pFeaClass1 = (pWs as IFeatureWorkspace).OpenFeatureClass(tableName);
    IFeatureClass pFeaClass2 = (pWs as IFeatureWorkspace).OpenFeatureClass(tableName);
    //释放其中的一个要素类
    Marshal.ReleaseComObject(pFeaClass1);
    //再次调用另外一个要素类会报错
    var name=(pFeaClass2 as IDataSet).Name;
    
  • 函数产生的临时对象忽略释放

    IDataset pDataset=pSaveAs.Save(参数1,参数2,参数3);
    //切记要释放SaveAs产生的零食对象
    Marshal.ReleaseComObject(pDataset);
    
  • 循环中的Com对象也要及时释放

    for(int i=0;i<10000;i++)
    {
    	IRow pRow=pTable.CreateRow();
    	......
    	//测试的Pow使用完毕记得释放
    	Marshal.ReleaseComObject(pRow);
    }
    

练习

  1. 利用游标查询数据,管理其中的Com对象。
  2. 另存栅格数据,管理生成的临时Com对象。
  3. 手写一个打开Shapefile,然后做简单的属性查询,体验Com对象的管理。
发布了93 篇原创文章 · 获赞 88 · 访问量 37万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章