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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章