Marshal.ReleaseComObject Is Considered Dangerous
Paul Harrington, Principal Developer on the Visual Studio Platform Team, has written an explanation on why calling Marshal.ReleaseComObject() to dispose of a COM object from managed code is considered dangerous and recommends not using it.
Harrington noticed a problem using Marshal.ReleaseComObject while converting some of the Visual 2010 components, the window manager, the command bars and the text editor, from native code into managed code. VS 2005 and 2008 have had these components written in native code and they worked fine until porting them to managed code.
When managed code wants to access COM functionality it does so through COM Interop. When a call to a COM object is made the CLR returns the object wrapped in a Runtime Callable Wrapper (RCW), an object obeying the rules that apply to managed objects including those related to Garbage Collection (GC). That means the RCW is freed up when the GC decides it is time for some cleaning, and if the application does not use many resources it could be possible the GC won’t do its job until very late or not at all until the application is shutdown. During that time RCW may hold a large COM object sitting undisposed, existing the risk it will leak if the application is shutdown and the GC has not run yet.
To avoid leaving undisposed COM objects, the COM Interop has provided the method Marshal.ReleaseComObject which decrements the reference counter of the RCW which counts how many clients have called the object. The method returns the new value of the reference count which is “typically zero since the runtime callable wrapper keeps just one reference to the wrapped COM object regardless of the number of managed clients calling it.” When that happens, the underlying resources consumed by the COM object are released as it should be.
This mechanism has worked well in previous versions of Visual Studio which have many Marshal.ReleaseComObject calls. But that has changed when some of the components have been rewritten in managed code. To maintain compatibility with the rest of the code, the new components are accessed via the Interop layer through a COM Callable Wrapper (CCW). So, the caller might think it is dealing with a native COM object but it is actually using a managed one. Everything works fine until the Marshal.ReleaseComObject call which was originally meant to release COM resources. The runtime throws an ArgumentException with the message “The object's type must be __ComObject or derived from __ComObject.” because the object to be released is not a COM one but a managed one.
There is yet another problem using Marshal.ReleaseComObject. When called, the method normally releases COM resources and usually returns zero. That means the COM object is unbound from its RCW wrapper. If later, another client wants to call the same COM object it will get an InvalidComObjectException with the message “COM object that has been separated from its underlying RCW cannot be used” if the RCW object was cached and not garbage collected. So, one must be sure to call Marshal.ReleaseComObject only it is certain the respective COM object is no longer going to be used again.
To solve the problem in VS 2010 the team had to remove all calls to Marshal.ReleaseComObject, recommending not using it. They have also created “patched versions of our Managed Package Framework for VS 2005 and VS 2008 so that, when loaded in VS 2010 they would not have ReleaseComObject problems. You’ll see these patched versions appear as binding redirects in “devenv.exe.config” for Microsoft.VisualStudio.Shell and Microsoft.VisualStudio.Shell.9.0.” While this problem has surfaced while working on VS 2010, it may be common for other projects where COM Interop is used.
Brandon Holt, Preston Briggs, Luis Ceze, Mark Oskin May 21, 2015
Kai Kreuzer, Olaf Weinmann May 21, 2015