假设我有一个 COM 对象(非托管)和 .NET 客户端.
Assume I have a COM object (unmanaged) and .NET Client.
是否需要从 .NET 客户端调用 Marshal.FinalReleaseComObject
方法才能释放 COM 对象?
Is it necessary to call from the .NET client Marshal.FinalReleaseComObject
method in order to release the COM object?
修改是"和否".
首先,请记住 ReleaseComObject
不会自动减少真正的 COM 对象引用计数.相反,它会减少内部 RCW 计数器.(每次 same COM 对象从 COM->NET 移出时,它都会使用 same RCW 并将 RCW 计数器加一.)同样,FinalRelaseComObject
也会影响 RCW 计数器并有效地将其设置为 0".当 RCW 计数器变为零时,.NET 将减少 actual COM ref-count.
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:
ReleaseComObject
,但不是 FinalReleaseComObject
,对其进行调用.也就是说,每次对象交叉时都应该调用它一次,即使它导致引用等于 RCW.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.我说上面的规则和不是 FinalReleaseComObject
因为RCW对象缓存在一个身份代理中,所以使用FinalReleaseComObject
会影响你甚至不知道的 COM->NET 边界交叉!(这很糟糕,在这一点上,我完全同意 JaredPars 的回答.)
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.)
而否",当一个RCW被回收时(终结器被调用),它会自动释放"COM引用(有效地调用FinalReleaseComObject
本身).请记住,由于只有一个 RCW 对象,这意味着只要在 .NET 中有对它的引用,它就永远不会被回收.如果回收 RCW,则 没有问题,因为从上面看,不再有对所述 RCW 的引用,因此 .NET 知道它可以将 COM 引用计数减少一(这可能会导致 COM要销毁的对象).
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).
但是,由于 .NET GC 是挑剔的且非确定性,因此依赖此行为意味着对象的生命周期不受控制.例如,在使用 Outlook 对象模型时,这可能会导致许多微妙的问题.(其他 COM 服务器可能不太容易出现问题,但 OOM 会在内部对对象进行有趣的缓存".如果不明确控制生命周期,很容易得到项目已被修改"异常.)
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.)
在我的代码中,我有一个支持 IDisposable
的 ComWrapper
类.这使我可以传递从 COM->NET 获得的具有明确生命周期所有权的 RCW 对象.在切换到这种方法后,我在 Outlook-Addin 开发中遇到的问题非常少(实际上几乎没有),并且之前有许多问题.缺点"是需要确定 COM->NET 边界,但一旦确定,那么内部所有生命周期都得到正确处理.
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.
编码愉快.
这篇关于.NET 和 COM 互操作性:从 .NET 客户端释放 COM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!