Developers who are accustomed to the IDisposable pattern or to C#’s ‘using’ syntax sometimes ask why COM Interop doesn’t support IDisposable on every Runtime Callable Wrapper (RCW). That way, managed code could indicate that it is finished using the unmanaged COM resource. This would allow the resources to be cleaned up much earlier than they would be if we waited for a GC. Also, it might better approximate the way an unmanaged client would have used this COM object through explicit Release calls.

There’s a service called System.Runtime.InteropServices.Marshal.ReleaseComObject() that looks suspiciously like it could be used as a Dispose() call. However, this is misleading. ReleaseComObject is quite different from Dispose() and it’s also quite different from IUnknown::Release() as I’ll explain.

The COM Interop layer in the CLR can make do with a single reference count against the unmanaged pUnk, regardless of how many managed clients refer to that object. In other words, the Interop layer does not hold a reference count for each managed client of that pUnk. Instead, we rely on the reachability magic of the GC to determine when nobody needs that pUnk anymore. When nobody needs the pUnk, then we drop our single reference count on that pUnk.

Furthermore, negotiation for interfaces in managed code via COM Interop does not necessarily affect the unmanaged refcount of the COM object. For instance, the managed wrapper might have already cached a pUnk for this interface.

Regardless of the actual refcount that the wrapper holds on the underlying COM object, ReleaseComObject will release all these refcounts at one time.

However, the return value from ReleaseComObject reveals that there’s an additional refcounting scheme involved. This is unrelated to the COM refcount. The same pUnk might be marshaled into the managed process a number of times. We keep track of this count. You can then call ReleaseComObject that same number of times before we will call IUnknown::Release on the pUnks held by the wrapper and start giving throwing InvalidComObjectExceptions. If you are passing the pUnk backwards and forwards across the layer, this means that the “marshaling count” will be a large and arbitrary number. But, for some usage patterns, the number of times the pUnk is marshaled across may correspond to the number of distinct managed clients that have got their hands on the wrapper. If this happens to be the case, then that many managed clients can independently call ReleaseComObject before the wrapper is zombied and the underlying pUnks are Release’d.

I guess that this behavior is slightly more useful than a simple Release in some circumstances. And you can turn it into the equivalent of IUnknown::Release by calling it in a loop until it returns 0. At that point, our internal “marshaling count” has been decremented to 0 and we have Release’d the pUnks. (We really need to add a ReleaseComObjectFully() service to avoid that silly loop).

Application code can either be on the GC plan, where we track whether there are references outstanding – but in a non-deterministic manner that is guided by memory pressure – or application code can do the tracking itself. But if the application does the tracking, it is responsible for knowing whether there are other managed clients still using the COM object.

One way you might do this is by subtyping the wrapper and adding a Dispose protocol on the managed side that is reference counted. But all managed clients in the process must observe the discipline you define. A more practical approach is to ensure that you are the only client of the pUnk by creating the COM object yourself and then never sharing that reference with anyone else.

If you are using a COM object in a scoped, single-threaded manner then you can safely call ReleaseComObject on that object when you are done with it. This will eagerly release any unmanaged resources associated with that object. Subsequent calls would get an InvalidComObjectException. So don’t make subsequent calls.

But if you are using a COM object from multiple places or multiple threads in your application (or from other applications in the same process), you should not call ReleaseComObject. That’s because you will inflict InvalidComObjectExceptions on those other parts of the application.

So my advice is:


  1. If you are a server application, calling ReleaseComObject may be an important and necessary requirement for getting good throughput. This is especially true if the COM objects live in a Single Threaded Apartment (STA). For example, ASP compatibility mode uses the DCOM STA threadpool. In these scenarios, you would create a COM object, use it, then eagerly call ReleaseComObject on each request.
  2. If you are a client application using a modest number of COM objects that are passed around freely in your managed code, you should not use ReleaseComObject. You would likely inflict Disconnected errors on parts of the application by doing so. The performance benefit of eagerly cleaning up the resources isn’t worth the problems you are causing.
  3. If you have a case where you are creating COM objects at a high rate, passing them around freely, choking the Finalizer thread, and consuming a lot of unmanaged resources… you are out of luck.
Comments (14)

  1. Sam Gentile says:

    Any comments? Do you read your readers? -)

    I am very interested in this discussion. Thanks for your blog. It is a bright light in the CLR world.

  2. Chris Brumme says:

    I agree… the marriage of refcounted COM and traced GC is an uneasy one. We actually spent a lot of time designing hybrid systems to provide deterministic finalization of managed objects based on injected refcounting by the JIT. The technical complexity to the CLR was very high; the performance looked like it would be painful; the programming model was lame; and ultimately the approach still didn’t give the application what it needed. As soon as a refcounted object was leaked into a traced reference, its lifetime became non-deterministic (at least until the next GC).

    We also considered breaking identity during marshaling. If we created a different ComObject every time a pUnk was marshaled into managed code, the application could call ReleaseComObject with impunity. However, this also polluted the programming model. And imagine the difficulty of calling ReleaseComObject on every reference you acquire in this manner.

    This leaves many COM Interop scenarios in a difficult position. Scenarios #1 & #2 from my post above really do work. It’s scenario #3 which fails. There are a couple of things we want to do to give some relief in the future.

    We want to handle the case where a managed 20-byte ComObject is holding onto a 4 MB unmanaged bit map via a COM object. Currently, the GC is unaware of the "value" of the unmanaged resource associated with the tiny ComObject and will delay collection. (Eventually we will notice the overall memory load on the system, but this is beyond the most efficient time for the collection).

    So we want to allow the developer to inform the GC of this cost so that we collect more aggressively. We would also like to provide some services that let you control how many of a scarce resource can be in use at one time.

    Obviously I can’t give details on either of those efforts. They should be generally useful outside of COM Interop. And they should also take some of the scenarios that are currently in #3 and move them to #2.

    That still leaves a messy problem with any scenarios that remain in bucket #3. I’ll talk to our Interop team and see if we can come up with patterns that allow different components to usefully agree on whether a COM object is still in use or whether it should be aggressively Release’d. But I’m not particularly hopeful that we’ll find a magic bullet.

  3. I wonder if I run into any trouble if use Single Threaded Apartment, an RCW and ThreadStatic together?

  4. ThoughtWorks says:

    In a test program, I call Mashal.ReleaseComObj several times and it return -1 at last. What this -1 means? Shouldn’t it always stop at 0?

  5. code weaver says:

    Hi Just wanted to know the semantics of Releasecomobject with COM+ usage

  6. Chris Brumme says:

    There is a detailed discussion of ReleaseComObject combined with EnterpriseServices (managed COM+) in chapter 8 of the excellent Prescriptive Architecture Guidance book on "Improving .NET Application Performance and Scalability".

    Chapter 8 can be found at There is also some general guidance about ReleaseComObject in Chapter 7.

  7. I recently was asked to take a look at some VSTO test automation that wasn’t behaving correctly on lab

  8. [Furl] [DotNetJunkies] [COM interop reference counting in .NET] Excel VBA code to convert collection into an array >Function collectionToArray(c As Collection) As Variant() Dim a() As Variant: ReDim a(0 To c.Count – 1) Dim i As Integer For i =…

Skip to main content