声 明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对象释放注意事项
- 优先使用
ComReleaser
管理Com对象 - 在使用Com对象的时候,最好就管理起来,避免遗漏
- 循环中产生的Com对象,建议及时回收,不要等循环完毕一次性回收
- 函数生成的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); }
练习
- 利用游标查询数据,管理其中的Com对象。
- 另存栅格数据,管理生成的临时Com对象。
- 手写一个打开Shapefile,然后做简单的属性查询,体验Com对象的管理。