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.)
