2010년 2월 5일 금요일

Release COM References

ArcObjects를 이용하여 Table(FeatureLayer, ITable, IFeatureClass 등)을 Query할 때 Search, Update 메쏘드를 활용한다.

공간 및 속성조건을 설정하기 위해 IQueryFilter(속성조건), ISpatialFilter(공간 및 속성조건)를 활용하며 결과는 ICursor, IFeaureCursor를 반환한다.

가끔 Cursor를 Loop문 안에 반복적으로 사용하는 경우(아래 예)가 있으며, 이 경우 Shapefile의 경우를 제외하고 Personal Geodatabase(Access, 일반적으로 255번째 발생), GeoDatabase(SDE)의 경우 예외없이 COMException이 발생한다. 여기에서는 이 해결방안을 알아보도록 한다.

[code c#]
IFeatureCursor afCursor = sourceFc.Search(null, true);
IFeature aFeature = afCursor.NextFeature();
while (aFeature != null)
{
    IQueryFilter filter = new QueryFilterClass();
    filter.WhereClause = string.Format("{0} = {1}", sourceFc.OIDFieldName, aFeature.OID);

    IFeatureCursor bfCursor = ipInputFc.Search(filter, true);
    IFeature bFeature = bfCursor.NextFeature();
    if (bFeature != null)
    {
        System.Diagnostics.Debug.WriteLine(bFeature.OID);
    }

    aFeature = afCursor.NextFeature();
}
[/code]

자세히 ..


일반적으로 이 문제는 Shared Schema Lock 문제와 관련이 있다.
.NET에서
COM object 역시 Garbage Collection 메커니즘(Garbage Collector)에 의해 객체가 관리되지만 Cursor의 경우에는 Data Source가 허락하는 최대 한계치까지 축적되며, 이 한계에 다다르면 오류를 발생한다.

다음 코드는 이 오류를 해결하는 예이며, 명시적으로 FeatureCursor를 해제하는 방법이다.

2가지 방법이 제공되며 다음과 같다.
1. System.Runtime.InteropServices.Marshal의 ReleaseComObject
2. ESRI.ArcGIS.ADF.ComReleaser의 ManageLifetime

[code c#]
IFeatureCursor afCursor;
IFeature aFeature;

//1. Marshal.ReleaseComObject
afCursor = sourceFc.Search(null, true);
aFeature = afCursor.NextFeature();
while (aFeature != null)
{
    IQueryFilter filter = new QueryFilterClass();
    filter.WhereClause = string.Format("{0} = {1}", sourceFc.OIDFieldName, aFeature.OID);

    IFeatureCursor bfCursor = ipInputFc.Search(filter, true);
    IFeature bFeature = bfCursor.NextFeature();
    if (bFeature != null)
    {
        System.Diagnostics.Debug.WriteLine(bFeature.OID);
    }
    System.Runtime.InteropServices.Marshal.ReleaseComObject(bfCursor);

    aFeature = afCursor.NextFeature();
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(afCursor);

//2. ComReleaser
using (ESRI.ArcGIS.ADF.ComReleaser comReleaser = new ESRI.ArcGIS.ADF.ComReleaser())
{
    afCursor = sourceFc.Search(null, true);
    comReleaser.ManageLifetime(afCursor);
    aFeature = afCursor.NextFeature();
    while (aFeature != null)
    {
        IQueryFilter filter = new QueryFilterClass();
        filter.WhereClause = string.Format("{0} = {1}", sourceFc.OIDFieldName, aFeature.OID);

        IFeatureCursor bfCursor = ipInputFc.Search(filter, true);
        comReleaser.ManageLifetime(bfCursor);
        IFeature bFeature = bfCursor.NextFeature();
        if (bFeature != null)
        {
            System.Diagnostics.Debug.WriteLine(bFeature.OID);
        }
        aFeature = afCursor.NextFeature();
    }
}
[/code]
만약 위와 같은 명시적으로 COM Object를 삭제한 후 이 Object를 사용하고자 할 경우에는 다음의 오류 코드를 반환한다.

RCW(Runtime Callable Wrapper)에 대한 내용은 다음을 참고하세요
COM Wrapper - RCW, CCW

※ See Also
일반적으로 Cursor(ICursor, IFeatureCursor) 인터페이스 외에도 ArcObjects의 Singleton objects들은 명시적으로 Release해야 한다.

Singletons(Singleton objects)는 하나의 인스턴스만을 지원하는 object를 말한다.
ArcObjects에서 Singleton objects는
Activator class를 이용해 생성하는 것이 좋으며, ComReleaser class(Marshal)를 통해 명시적인 해제가 필요하다.

또한 ArcObjects의 Singleton objects는 Thread, Process Singleton으로 구분되며 다음의 URL을 참고하면 된다.


ArcObjects의 Singleton Object는 다음의 URL을 참고하세요
Writing multithreaded ArcObjects code
Singleton objects