Until recently, I believed that the .NET runtime only increases the reference count of COM objects by 1 when creating a runtime-callable wrapper, and that only one such runtime-callable wrapper is created for any given COM object.
If I'm not mistaken, the above implies that Marshal.FinalReleaseComObject and Marshal.ReleaseComObject do the same thing in practice.
However, today I was writing some tests to verify that COM objects are properly released by my code. I do this by invoking the supposedly released object and checking for the expected InvalidComObjectException
. It turns out that there are cases where the exception is thrown after a FinalReleaseComObject
, but not after a ReleaseComObject
.
Does this mean that the .NET 2.0 runtime can hold more than one reference to a COM object? If so, when does it do this?
You can do this by putting break points or using print(CFGetRetainCount(CFTypeRef!)) function in your code . You can also increment the reference count of an Object using the CFRetain function, and decrement the reference count using the CFRelease function. CFRetain(CFTypeRef cf);CFRelease(CFTypeRef cf);
You should use this method to free the underlying COM object that holds references to resources in a timely manner or when objects must be freed in a specific order. Every time a COM interface pointer enters the common language runtime (CLR), it is wrapped in an RCW.
The common language runtime exposes COM objects through a proxy called the runtime callable wrapper (RCW). Although the RCW appears to be an ordinary object to . NET clients, its primary function is to marshal calls between a . NET client and a COM object.
There's an extra level of indirection here. Yes, the RCW keeps a single reference count on the native COM interface pointers. But the RCW has a reference count too, it is incremented every time a COM interface pointer is mapped to the RCW. Which may happen if a COM method returns an interface pointer. The finalizer of the corresponding .NET wrapper class decrements it.
You can tinker with that reference count directly through Marshal.ReleaseComObject(), which decrements it by one like the finalizer does, and Marshal.FinalReleaseComObject(), which zaps it to zero, guaranteeing that the IUnknown::Release() method is called. They of course fall in the "better know what you're doing" category. Getting it wrong produces the ugly and undebuggable "COM object separated from its underlying RCW" exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With