.NET and COM Interoperability : release COM from .

2020-02-13 14:10发布

问题:

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:

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.



回答2:

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.