Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I'm developing a plugin workspace for ArcGIS. Internally, the plugin uses an OleDBconnection. All of the sample code I see seems to stress the importance of closing and disposing the connection.

When the client retrieves all rows from my plugin cursor, this is not a problem - when OleDBDataReader.Read() returns false, I close the connection and set IPluginCursorHelper.IsFinished to true.

However, in the case where the client doesn't retrieve all of the rows I am not able to find an appropriate place to close the connection.

Since the client is required to call ReleaseCOMObject when he's finished with the cursor, is there some way the plugin cursor could be informed of this event and close the OleDBConnection OleDbReader if it is still open?

I tried implementing IDisposable, but ArcGIS doesn't call dispose. I tried using a destructor, but when I try to close the connection there I get an exception saying "COM object that has been separated from its underlying RCW cannot be used."

Update

After having no luck with the COM hacks offered in the comments, I changed my code so that now the IPluginDatasetHelper is responsible for opening the OleDBConnection the first time a cursor is requested. It passes the open connection the cursor's constructor, which keeps it as a member variable. Now the problem is this: how can the IPluginDatasetHelper close the OleDBConnection?

Fortunately, the issue is no longer anywhere nearly as urgent - apparently only one instance of the IPluginDatasetHelper ever gets created. The test code below works without a memory leak, and the IPluginDatasetHelper's constructor only gets called once. When justone is true, the test code would show severe memory leakage when the cursor is responsible for opening the connection and justOne true. With the dataset helper responsible for opening the connection and justOne true it climbs to 200MB and stabilizes. Still, it would be nice to know where to close the connection in the dataset helper.

for (int i = 0; i < 1000; i++)
{
    Type t = Type.GetTypeFromProgID("esriGeoDatabase.MyPluginWorkspaceFactory");
    var factory = Activator.CreateInstance(t) as IWorkspaceFactory;
    var fws = factory.OpenFromFile(dbcPath,0) as IFeatureWorkspace;
    var fc = fws.OpenFeatureClass(tableName);
    IFeatureCursor fCur = fc.Search(null, false);
    IFeature feat;

    while ((feat = fCur.NextFeature()) != null)
    {
        if (justone)
            break;
    }    
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
}

Update 2

Running the test in the VS debugger, I get this rather strange exception. I'm hoping it is a result of running it in the debugger from a console exe and will not happen within arcmap. (10000 iterations for test loop).

ConextSwitchDeadlock was detected. The CLR has been unable to transition from COM context 0x4cd7fe8 to COM context 0x4cd8158 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

Update 3

I've refactored so that the connection is now a member of the Dataset class and an OleDbDataReader is a member of the cursor helper class. I'm not sure how critical it is to close the connection in the dataset helper. IPluginDatasetHelper has an Open - but no Close. Now the question is how do I close the OleDbDataReader. (Original question was OleDbConnection.)

share|improve this question
    
Very interesting question. Looks like no way to handle this is implemented in .NET. There was an interesting discussion going on at stackoverflow: How to dispose of a NET COM interop object on Release(). Another one: Is it possible to intercept (or be aware of) COM Reference counting on CLR objects exposed to COM. –  Petr Krebs Dec 15 '12 at 18:01
    
After diving into the linked answers, this one seems to be the most viable option. Also see this reusable adaptation of the same solution. Would recommend an extensive testing, though, due to the inherent risk given it is essentialy a hack. –  Petr Krebs Dec 15 '12 at 20:45
    
Thanks @PetrKrebs, I implemented the code in the link, but neither NewAddRef nor NewRelease get called. Both "this one" and "this" are linked to the same answer ... is there another link you intended to insert? –  Kirk Kuykendall Dec 16 '12 at 4:27
    
@PetrKrebs After setting ComVisible(true) for the plugincursorhelper, the NewAddRef is now being called, unfortunately the NewRelease is not being called. I'm puzzled how ArcGIS handles this when it's not ComVisible. The plugincursorhelper in the SimplePoint sample is not COM visible. –  Kirk Kuykendall Dec 16 '12 at 4:48
    
Sorry, here's the correct link for the second answer. It does seem to handle additional interfaces, which could be helpful in your case. –  Petr Krebs Dec 16 '12 at 10:36

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.