从 .NET 安全地释放 COM 对象引用

发布于 2024-08-13 02:06:08 字数 949 浏览 3 评论 0原文

我在网上读了很多关于安全释放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);
    }
}

这样做是否足够?

I have read a lot of articles on the net about releasing RCW's safely, and it seems to me that no one can agree on exactly what needs to be done in what order, so I'm asking you guys for your opinions. For example, one could do this:

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();
    }
}

However, some people advocate doing the garbage collection before Marshal.FinalReleaseComObject, some after, and some not at all. Is it really necessary to GC every single RCW manually, especially after it has already been detached from its COM object?

To my mind, it would be simpler and easier to just detach the RCW from the COM object and leave the RCW to expire naturally:

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

Is it sufficient to do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

秋心╮凉 2024-08-20 02:06:08

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

这就留下了一个更大的问题:为什么人们提倡调用 GC.Collect()WaitForPendingFinalizers()

因为对于某些设计,很难知道何时不再有托管引用,因此您无法安全地调用 ReleaseComObject。你有两个选择,让记忆积累并希望收集发生或强制收集。 [请参阅评论中 Steven Jansen 的注释]

附加说明是,将 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”将在“bye”之后打印出来,而在 Collect() 之前将 x 设置为 null 会已经“修复”了这个问题。

To have your reference to the target COM object released, it is sufficient and preferred to just call Marshal.FinalReleaseComObject and not force a collect. In other words, you've met your responsibility to release your reference as soon as you were done with it. I won't touch the issue of FinalReleaseComObject vs ReleaseComObject.

This leaves the bigger question of why do people advocate calling GC.Collect() and WaitForPendingFinalizers()?

Because for some designs, it's hard to know when there are no more managed references so you can't safely call ReleaseComObject. You have two choices, let the memory build up and hope a collect happens or force a collect. [see Steven Jansen's note in the comments]

An additional note is that setting target to null is usually unnecessary, and specifically is unnecessary in your sample code. Setting objects to nothing is common practice for VB6 since it uses a reference count based garbage collector. The compiler for C# is clever enough (when building for release) to know that target is unreachable after its last use and could be GC'd, even before leaving scope. And by last use, I mean last possible use so there are cases where you might set it to null. You can see this for yourself with the code below:

   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");
       }
   }

If you build release (e.g., CSC GCTest.cs), "Finalized" will print out between "hello" and "bye". If you build debug (e.g., CSC /debug GCTest.cs), "Finalized" will print out after "bye" whereas setting x to null prior to Collect() would have "fixed" that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文