tl;dr; Avoid using
Marshal.ReleaseComObject when possible.
When you use a COM object in .NET the .NET framework creates a Runtime Callable Wrapper (RCW) which shows up in the debugger as
__ComObject. This allows interoperation between managed .NET and native COM code. One of the major differences between C++ COM and .NET is that in C++ you have deterministic destruction while in .NET objects are finalized at the garbage collector’s leisure. Which mode of operation is preferable is a matter of taste and not the topic of this post (I’ll just say that you can take my deterministic destruction when my cold dead hand goes out of scope) however mixing the two modes can cause problems.
The SOP is that the RCW calls
AddRef on the COM object it is holding so that it won’t disappear while it’s being used and then, when the RCW is collected by the GC, it will release this reference in its finalizer. This works if you don’t care exactly when the COM object is released and .NET exposes
Marshal.ReleaseComObject for cases in which you want to manually control the object’s lifetime.
We recently re-implemented a few interfaces (that were previously native COM) in .NET and stuff stopped working. A bit of debugging later I find that we’re calling
ReleaseComObject on an object that is now implemented in .NET, this causes an
ArgumentException to be thrown. A cow-orker pointed me at a post titled Marshal.ReleaseComObject Considered Dangerous (well at least it’s not harmful), this post describes the problem we were facing and the solution I already put in place (check with
Marshal.IsComObject before calling
ReleaseComObject) but goes on saying that this isn’t enough since once you call
ReleaseComObject the RCW is disconnected from the COM object and can’t be used (and that this can be a problem if the RCW is cached). I thought that this is a theoretical problem (I know we aren’t caching the RCWs) and kept trying to figure out why our code still wasn’t working.
It turns out that as in everything in programming it’s all about adding another level. We had three levels, a .NET class (which we’ll call A) a native class (C) and class B which was native in a previous incarnation but is now .NET, A holds a reference to B which hands it a C (via a method or property), but why is this a problem?
For that we’ll need to dive a bit deeper into how RCWs work.
In order to preserve COM’s identity semantics whenever a COM object is passed into the CLR the .NET framework first checks if there is an active RCW for this object, if so it returns the existing object. You can see this by debugging your native object, after an object is returned from a native COM method (or property) it is followed by a
IUnknown and then by two calls to
Release (one for the reference gained in the original function and one for the reference gained in
QueryInterface). Additionally the RCW maintains a count of the times it has been reused, MSDN refers to this as a reference count but I’ll refer to it as marshal count in order to emphasise the difference between this number and the native COM object’s reference count. If an RCW is reused at the identity checking phase the marshal count is incremented. When
ReleaseComObject is called on an RCW the marshal count is decremented. If the marshal count reaches zero the RCW releases its references to the native COM object and marks itself as invalid, any subsequent calls to the wrapped object will throw an
InvalidComObjectException with the message “COM object that has been separated from its underlying RCW cannot be used”. If an RCW is collected by the GC while it is still in a valid state it will also release it’s wrapped object (regardless to its marshal count).
Now consider the situation we had when B was a native COM object. Whenever A got a C from B said C had crossed the CLR boundary and a new RCW was created (or at least the marshal count was incremented). Then when A was done with C it called
ReleaseComObject and all was fine and dandy.
In order to highlight the point the following sequence diagram shows what happens if A acquires C several times in a loop. The RCWs (in blue) represent the boundary between native code and the CLR.
But now B is managed so A‘s RCW was created once when B acquired it. Now when B hands C to A it is passing a regular managed reference (that of the RCW), .NET references aren’t aware of being copied (there’s no copy-constructor to trigger a change in the marshal count) and when A will call
ReleaseComObject it will invalidate A without B having a clue that its member is no longer valid. Note the fact that the incrementation and decrementation of the marshal count are not balanced (in respect to the loop).
We have quite a few places which use
ReleaseComObject, some of which are probably cargo cult programming but some are needed, how should we make sure that we detect all the problematic uses of
The obvious solution is the one Microsoft chose, identify all the places you use
ReleaseComObject and remove them unless they can be proven to be correct (this is also the route we chose). I’ll list a few other options we thought of so other people can be informed of all (?) their options.
Adopt .NET Semantics
Embrace the fact that destruction isn’t deterministic and make sure your code doesn’t depend on deterministic destruction, then remove all calls to
- Pro: You gain a consistent world view, your code is easier to reason about
- Con: This can be very difficult and require a lot of work
Artificially Increment the Marshal Count
You can manually force the marshal count to be updated and do this for each RCW you hand out.
public static T AddRcwRef<T>(T t)
IntPtr ptr = Marshal.GetIUnknownForObject(t);
Marshal.Release(ptr); // done with the IntPtr
- Pro: You don’t have to change your client code (perhaps you can’t)
- Con: Every developer in the organization must be familiar with this (anti) idiom and maintain its use in the correct places. Also if you really do need deterministic destruction you’ll have to be sure to call
ReleaseComObject on your member at the correct time otherwise the COM object won’t get destroyed.
Get the Finalizer to do the work
Instead of calling
ReleaseComObject discard your reference to the object and let the GC check if it’s still in use.
public static void ReleaseRcw<T>(ref T t)
t = default(T);
- You gain a submission to TDWTF
- If you thought of this you’ve probably exhausted all your other options and it’s time to choose the least bad one.
- If the object in question is an argument to a function the call stack will have a reference and this won’t work
- Performance killer
This brings us back to the tl;dr; when writing new code try to avoid using
ReleaseComObject, it may save you a lot of trouble in the long run.
 Actually it may call
AddRef several times but it calls
Release for them all at the same point in time, in my test project the RCW held on to 4 references to the COM object. ↩
 According to Eric Lippert on my stackoverflow question this function can be replaced with:
Marshal.GetIUnknownForObject(o); // ignore return value and he should know what he’s talking about but I think the original version is more explicit.↩