聲 明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對象的管理。