【问题标题】:Releasing a COM object reference safely from .NET从 .NET 安全地释放 COM 对象引用
【发布时间】:2009-12-02 18:41:30
【问题描述】:

我在网上阅读了很多关于安全发布 RCW 的文章,在我看来,没有人能就具体需要按什么顺序完成的事情达成一致,所以我想请教各位大佬的意见。例如,可以这样做:

object target = null;
try {
    // Instantiate and use the target object.
    // Assume we know what we are doing: the contents of this try block
    // do in fact represent the entire desired lifetime of the COM object,
    // and we are releasing all RCWs in reverse order of acquisition.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
        target = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

然而,有些人主张在Marshal.FinalReleaseComObject 之前进行垃圾收集,有些人在之后,有些人则根本不这样做。真的有必要手动 GC 每一个 RCW,尤其是在它已经从它的 COM 对象中分离之后?

在我看来,将 RCW 从 COM 对象中分离出来并让 RCW 自然过期会更简单、更容易:

object target = null;
try {
    // Same content as above.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
    }
}

这样做就足够了吗?

【问题讨论】:

    标签: .net com-interop rcw


    【解决方案1】:

    要释放您对目标 COM 对象的引用,只需调用Marshal.FinalReleaseComObject 并且强制收集就足够了并且首选。换句话说,您已经履行了在完成参考后立即发布参考的责任。 FinalReleaseComObject vs ReleaseComObject的问题我就不说了。

    这就留下了一个更大的问题,为什么人们提倡打电话给GC.Collect()WaitForPendingFinalizers()

    因为对于某些设计,很难知道何时不再有托管引用,因此您不能安全地调用 ReleaseComObject。你有两个选择,让记忆建立起来,希望收集发生或强制收集。 [见 Steven Jansen 在 cmets 中的反对票]

    另外需要注意的是,将target 设置为null 通常是不必要的,尤其是在您的示例代码中是不必要的。将对象设置为空是 VB6 的常见做法,因为它使用基于引用计数的垃圾收集器。 C# 的编译器足够聪明(在为发布而构建时)知道target 在最后一次使用后是无法访问的,并且可能会被 GC,甚至在离开范围之前。最后一次使用是指最后一次可能的使用,因此在某些情况下您可以将其设置为null。您可以使用以下代码自己查看:

       using System;
       class GCTest
       {
           ~GCTest() { Console.WriteLine("Finalized"); } 
           static void Main()
           {
               Console.WriteLine("hello");
               GCTest x = new GCTest();
               GC.Collect();
               GC.WaitForPendingFinalizers();
               Console.WriteLine("bye");
           }
       }
    

    如果您构建版本(例如,CSC GCTest.cs),“Finalized”将在“hello”和“bye”之间打印出来。如果您构建调试(例如,CSC /debug GCTest.cs),“Finalized”将在“再见”之后打印出来,而在 Collect() 之前将 x 设置为 null 将“修复”该问题。

    【讨论】:

    • 投反对票:这些人都反对GC.Collecta Microsoft Distinguished Engineerthe Visual Studio Teama highly ranked SO member
    • @Steve:感谢您的链接。我已经更新了答案以包含对您的警告的引用。
    • @SteveJansen - 前两个链接建议致电GC.Collect。第一个也说“我们确实需要添加一个 ReleaseComObjectFully() 服务” - 后来添加为 Marshal.FinalReleaseComObject,这是 Tony Lee 上面推荐的。 MS 链接关注当 COM 对象被重写为非 COM 对象时会发生什么,或者当它对多个客户端可见时,因此必须仔细管理清理。这些担忧通常并不适用。
    猜你喜欢
    • 2015-09-25
    • 2012-09-17
    • 2010-11-29
    • 2015-07-09
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多