当创建 COM 对象的线程终止时,CLR 是否会释放 COM 对象

本文关键字:COM 对象 CLR 是否 释放 终止 创建 线程 | 更新日期: 2023-09-27 18:34:37

我一直无法弄清楚如何搜索对这种怀疑的确认,但我看到证据表明,一旦代码停止在创建它的线程上执行(并且该线程可能已终止(,在一个线程上创建的 COM 对象不再可用于其他线程(无法使用与其底层 RCW 分离的 COM 对象(。这是一个非常隐蔽的问题,因为我在整个代码中都有调用System.Runtime.InteropServices.Marshal.ReleaseComObject,但我无法确定正在调用它们中的任何一个导致此错误。最后,我得出的结论是,当辅助线程停止执行时,COM 对象显然被隐式释放。这是真的吗?这是记录在案的行为吗?

当创建 COM 对象的线程终止时,CLR 是否会释放 COM 对象

是的

,COM 对象往往具有很强的线程相关性。 线程不是 COM 中的次要实现细节。 与 .NET 不同,COM 为 COM 类提供线程安全保证。 COM 可以发布它支持的线程类型,其中"apartment"(即"非线程安全"(是一个非常常见的选择。COM 确保满足这些要求,而无需程序执行任何操作来提供帮助。 自动将调用从一个线程封送到另一个线程,以便始终以线程安全的方式使用对象。 在 .NET 代码中,您通常必须自己执行此操作,例如使用 Control.BeginInvoke 或 Dispatcher.BeginInvoke。

这样做的自动结果是,拥有一个或多个允许退出的 COM 对象的线程将自动释放这些对象。 这是必要的,因为不再有办法满足线程安全要求。 无论如何,在此之后尝试使用它们都会爆炸。 除了确保线程保持活动足够长的时间以继续为这些对象提供服务之外,没有其他方法可以解决这个问题。 同样,您需要使 UI 线程保持活动状态足够长的时间,以确保 Dispatcher.BeginInvoke 仍然可以在 .NET 中工作。

Fwiw,是的,使用 Marshal.ReleaseComObject(( 可以给你很多关于这个的麻烦。 显式内存管理在产生错误程序方面有着悠久的历史,自动垃圾回收提供了治疗方法。 GC非常有能力在没有您帮助的情况下释放该COM对象,并且永远不会出错。 只是需要更长的时间才能解决它。 如果您知道 COM 对象的资源使用率异常高,因此可以确定性地释放它,那么您将执行与对昂贵的 .NET 对象图完全相同的操作:GC。Collect(( 对此有所帮助。 检查这个答案,了解为什么Marshal.ReleaseComObject((倾向于不必要地使用。

下面是我设法重现 Hans Passant 答案中的行为的示例代码。我可以单击按钮 1 创建一个对象,然后在创建线程终止后单击按钮 2 访问它时,出现错误"无法使用与其基础 RCW 分离的 COM 对象"。

Public Class Form1
   Dim comRef As Microsoft.Office.Interop.Outlook.Application
   Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      Dim t As New System.Threading.Thread(AddressOf CreateApplication)
      t.SetApartmentState(Threading.ApartmentState.STA)
      t.Start()
   End Sub
   Private Sub CreateApplication()
      comRef = New Microsoft.Office.Interop.Outlook.Application
   End Sub 
   Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
      TextBox1.Text = comRef.DefaultProfileName
   End Sub
End Class