发布COM组件

时间:2020-03-06 15:04:32  来源:igfitidea点击:

当我们不再需要通过调用Marshal.ReleaseComObject(..)来从Office PIA中释放COM组件时,是否真的有必要?

我在网上发现了与此主题有关的各种相互矛盾的建议。我认为,由于Outlook PIA总是返回对其接口的新引用作为其方法的返回值,因此没有必要显式释放它。我对吗?

解决方案

这里有一些使用托管包装器的好方法。值得检查一下。

也许这只是我的迷信,但我决定通过Marshal.ReleaseComObject()明确发布Office PIA,因为当我的应用程序崩溃时,对Excel和Word的引用仍然保持打开状态。我并没有深入探讨为什么(愚蠢的截止日期),但是将其作为班级处置模式的一部分来发布解决了该问题。

PIA是.NET互操作包装器。这意味着在对象的析构函数中(或者我不记得的Dispose)将自动处理其引用计数。诀窍是,在执行垃圾收集器之前,不会释放某些引用。这取决于COM对象实例化的对象。例如,打开数据库游标的COM对象将使这些游标在内存中保持活动状态,直到释放这些游标上的引用计数为止。使用.NET / COM互操作时,直到垃圾回收器执行或者使用Marshal.ReleaseComObject(或者FinalReleaseComObject)显式释放引用后,这些引用才被释放。

我个人还没有使用Microsoft Office PIA,但是在大多数情况下,我们不必显式发布引用。只有在应用程序开始锁定其他资源或者崩溃时,我们才应该开始怀疑悬挂引用。

编辑:如果遇到确实需要清理COM / Interop对象的情况,请使用Marshal.FinalReleaseComObject,该方法将引用计数始终保持为零,而不是仅递减一并将对象引用设置为null。如果我们确实想确保安全,则可以显式强制垃圾回收(GC.Collect),但请小心执行GC,因为它会引起明显的性能损失。

如果要退出Office应用程序的实例,则确实需要这样做,如本文章中所述。

除了最简单的方案外,很难在所有情况下都做到这一点。

通常,使用Microsoft Office,我们确实需要显式发布引用,可以分两个阶段安全地完成此操作:

(1)首先通过调用GC.Collect(),然后再调用GC.WaitForPendingFinalizers(),释放我们没有持有命名对象变量的所有次要对象。 (如果所涉及的对象可能具有终结器,例如在使用Visual Studio Tools for Office(VSTO)时,则需要调用两次)。

(2)然后通过调用每个对象上的Marshall.FinalReleaseComObject()显式释放持有命名变量的对象。

而已。 :-)

我在先前的文章中以及代码示例中对此进行了更详细的讨论。

我的经验表明,我们必须这样做,否则(至少是Outlook)该应用程序可能根本无法关闭。

但这打开了另一罐蠕虫,因为看起来RCW是每个进程的蠕虫,因此我们可以破坏其他加载项,这些加载项恰好具有对同一对象的引用。

我在这里发布了一个相关的问题,但是我仍然没有明确的答案。我知道更多后,我将编辑这篇文章。

关于.Net / COM互操作有一个简单的规则如有疑问,请始终使用Release()。 :-)

对于VS 2010,请参阅Marshal.ReleaseComObject被视为危险。