7

Assume I have a COM object (unmanaged) and .NET Client.

Is it necessary to call from the .NET client Marshal.FinalReleaseComObject method in order to release the COM object?

1

2 Answers 2

3

A modified "Yes", and a "No".

First off, bear in mind that ReleaseComObject does not automatically decrease the real COM object ref-count. Rather it decreases the internal RCW counter. (Every time the same COM object is moved from COM->NET it will use the same RCW and increment the RCW counter by one.) Likewise, FinalRelaseComObject also affects the RCW counterand effectively "sets it to 0". It is when the RCW counter goes to zero that .NET will decrease the actual COM ref-count.

So, "yes", but modified for these rules:

  1. Every time an object cross from COM->NET it should have ReleaseComObject, but not FinalReleaseComObject, invoked upon it. That is, it should be invoked once for each time the object crosses, even if it results in a reference-equals RCW.
  2. The number of references to the RCW (which is just a proxy wrapper) do not matter; only the times the object has crossed the boundary. See rule #1. (It is important to control references and who keeps them, but the "worst" that happens in this case is an exception from using a "detached RCW" which is no different than using a Disposed stream.)

I say the rules above and not FinalReleaseComObject because the RCW objects are cached in an identity proxy, so using FinalReleaseComObject will affect COM->NET boundary crossings you didn't even know about! (This is bad and, on this point, I entirely agree with JaredPars answer.)

And "no" in that, when an RCW is reclaimed (the finalizer is called), it will automatically "release" the COM reference (effectively call FinalReleaseComObject on itself). Remember that since there is only one RCW object this means that it will never be reclaimed as long as there is a reference to it in .NET. If the RCW is reclaimed there is no problem because, from above, there are no more references to said RCW and thus .NET knows it can decrease the COM ref-count by one (which may cause the COM object to be destroyed).

However, since the .NET GC is finicky and non-deterministic, relying on this behavior means that object-lifetimes are not controlled. This can cause many subtle issues when working with the Outlook Object Model, for instance. (It may be less prone to issues with other COM servers, but the OOM does funny "caching" of objects internally. It is easy to get a "Item has already been modified" exception when not controlling lifetimes explicitly.)

In my code I have a ComWrapper class that supports IDisposable. This allows me to pass around RCW objects obtained from the COM->NET with clear lifetime ownership. I have had very few issues (almost none, actually) in my Outlook-Addin development after switching to this approach and numerous issues before hand. The "downside" is that COM->NET boundary needs to be determined, but once that is, then internally all the lifetimes are correctly handled.

Happy coding.

Sign up to request clarification or add additional context in comments.

Comments

2

No. There is no need to explicitly release a COM object from a .Net client. The COM object will be collected like any other .Net object and will release it's underlying native handle once all references to it are removed.

Explicit use of FinalReleaseComObject can actually lead to programming errors. If another component in your code is still referencing the COM object you will be yanking out the native object out from under it. That will possibly lead to runtime failures down the road.

6 Comments

While "in theory" there may be no need, "in practice", there definitely is. Also, don't use FinalReleaseComObject, but rather ReleaseComObject for each time any COM object has been brought over the COM-.NET boundary. I do Outlook Addin programming: and I can say, giving objects explicit lifetimes is the only way to create a working add-in. Period. (I use a helper/wrapper class that supports Dispose I use with using.)
Wrong practice. You GC.Collect() + GC.WaitForPendingFinalizers() if you have to, never gets it wrong.
@HansPassant That doesn't work with the Outlook Object Model. Various related "solutions" involve trying to invoke that sequence twice in a row. It just does not work "in practice" (in the cases I deal with).
@pst if GC.Collect + GC.WaitForPendingFinalizers doesn't work but FinalReleaseComObject does then your FinalReleaseComObject is in all likelyhood orphaning a managed reference to the COM object somewhere in your program.
There's only one kind of COM, it isn't different for Outlook. Maybe you ought to ask a question about it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.