Sometimes your code requires an unmanaged resource, such as a file handle, a COM wrapper, or a SQL connection. A Using block guarantees the disposal of one or more such resources when your code is finished with them. This makes them available for other code to use.
Managed resources are disposed of by the .NET Framework garbage collector (GC) without any extra coding on your part. You do not need a Using block for managed resources. However, you can still use a Using block to force the disposal of a managed resource instead of waiting for the garbage collector.
A Using block has three parts: acquisition, usage, and disposal.
�?� Acquisition means creating a variable and initializing it to refer to the system resource. The Using statement can acquire one or more resources, or you can acquire exactly one resource before entering the block and supply it to the Using statement. If you supply resourceexpression, you must acquire the resource before passing control to the Using statement.
�?� Usage means accessing the resources and performing actions with them. The statements between Using and End Using represent the usage of the resources.
�?� Disposal means calling the Dispose method on the object in resourcename. This allows the object to cleanly terminate its resources. The End Using statement disposes of the resources under the Using block's control.
try { IFeatureWorkspace fws = (IFeatureWorkspace)OpenWS(@"D:\Projects\SAWS\data\March02Test1.gdb"); IFeatureClass fc1 = fws.OpenFeatureClass("SAWS_ADDRESSES"); IFeatureClass fc2 = fws.OpenFeatureClass("SAWS_ADDRESSES"); if (fc1 == fc2) Debug.Print("same object"); else Debug.Print("different object"); //Marshal.FinalReleaseComObject(fc1); Marshal.ReleaseComObject(fc1); Debug.Print("{0} records", fc2.FeatureCount(null)); } catch (Exception ex) { Debug.Print(ex.Message); }
try { IFeatureWorkspace fws = (IFeatureWorkspace)OpenWS(@"D:\Projects\SAWS\data\March02Test1.gdb"); IFeatureClass fc1 = fws.OpenFeatureClass("SAWS_ADDRESSES"); using (ComReleaser comReleaser = new ComReleaser()) { IFeatureClass fc2 = fws.OpenFeatureClass("SAWS_ADDRESSES"); if (fc1 == fc2) Debug.Print("same object"); else Debug.Print("different object"); comReleaser.ManageLifetime(fc2); Debug.Print("{0} records", fc2.FeatureCount(null)); } // next line throws exception ... Debug.Print("{0} records", fc1.FeatureCount(null)); } catch (Exception ex) { Debug.Print(ex.Message); }
Hi Gary,
Yes, managing Runtime Callable Wrappers (RCWs) can be tricky. However, depending on what you are doing, you may not need to manage them at all. As you know RCW's will hold a reference to their underlying COM object. When the RCW's finalizer is run, they will release their reference.
So when do RCW's get finalized? At a minimum, they get finalized when the AppDomain is torn down. During teardown, all objects are collected regardless of whether they are rooted. Before AppDomain teardown, there are two other ways that RCWs could be finalized. The first would be when the objects are no longer rooted and therefore become eligable for collection. If the GC0 heap fills up, a garbage collection will occur, and those RCWs that are eligible will be finalized. The second way finalization can happen is if you explicitly force a garbage collection by calling GC.Collect. If you do that, any RCWs eligible for collection will be finalized. By calling WaitForPendingFinalizers, you ensure that the finalizer thread has finalized all of the objects in the queue before your thread continues.
In addition, as you are aware, you can deterministically force the RCWs to release their reference by calling either Marshal.ReleaseComObject or Marshal.FinalReleaseComObject. The difference between the two calls is this. RCW's have a reference count of their own which gets bumped when the IUnknown is marshalled across AppDomain boundaries. Calling Marshal.ReleaseComObject will only actually release when the RCW reference count goes to zero--otherwise it has the effect of decrementing this internal count. Marshal.FinalReleaseComObject, however, will call the release regardless of what the RCW reference count is.
So the real question is when do you need to be explicit about enforcing RCW finalization or calling Marshal.Final/ReleaseComObject? The answer is whenever you can't afford to wait for GC to happen (knowing that it might not occur until shutdown). The two most likely reasons would be if the object is holding onto a resource (such as a file handle) or if memory pressure caused by keeping the object(s) alive was hurting performance.
If you know you are going to need to deterministically control the RCW cleanup, the best thing to do is to keep them isloated (and not pass them around) so that you can just call Marshal.FinalReleaseComObject when you are done with them. As long as you can guarantee that you won't try to call into the RCW again after you make that call, then this is a safe approach. This is better than trying to force a GC yourself since doing that will promote any existing objects in the heap to later generations which will mean they will potentially hang around in memory longer than they would have otherwise.
That said, the ideal is to do nothing and just let the system take care of everything. Managing this stuff on your own is harder, so be sure you understand your reasons for doing so before you take that on.
Sincerely,
Geoff Darst
Microsoft VSTO Team
Apparently ComReleaser calls FinalReleaseComObject when it disposes [...]
This might be a hard bug to track down. I've never seen a case where failure to releasecomobject on a featureclass causes problems, so maybe releasing is not worth the risk.
I'm with the same problem... using ArcGIS 10.2.2 and visual studio 2012 ....
you solved it???
thanks!!!