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

时间:2013-10-08 14:59:25

标签: c# .net vb.net com com-interop

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

2 个答案:

答案 0 :(得分:6)

是的,COM对象往往具有强大的线程关联性。线程不是COM中的次要实现细节。与.NET不同,COM为COM类提供了线程安全保证。 COM可以发布它支持的线程类型,“公寓”(即“非线程安全”)是一种非常常见的选择。 COM确保满足这些要求,而无需程序必须提供任何帮助。从一个线程到另一个线程的调用,以便始终以线程安全的方式使用该对象是自动的。在.NET代码中,您通常必须自己使用Control.BeginInvoke或Dispatcher.BeginInvoke这样做。

这样做的一个自动结果是拥有一个或多个允许退出的COM对象的线程将自动释放这些对象。这是必要的,因为不再有办法满足线程安全要求。在这之后试图使用它们会炸弹。除了确保线程保持足够长的时间以保持对这些对象的维护之外,没有办法解决这个问题。同样,您需要保持UI线程的活动时间足够长,以确保Dispatcher.BeginInvoke仍然可以在.NET中工作。

Fwiw,是的,使用Marshal.ReleaseComObject()可以为你提供大量的反馈。显式内存管理具有生成错误程序的悠久历史,并且自动垃圾收集提供了治愈。 GC非常有能力在没有你帮助的情况下发布COM对象,并且永远不会出错。它只需要更长的时间来解决它。如果您知道COM对象具有异常高的资源使用率,并且需要确定性地释放它,那么您执行与为昂贵的.NET对象图执行完全相同的操作:GC.Collect()有助于此。检查this answer以了解Marshal.ReleaseComObject()往往被不必要地使用的原因。

答案 1 :(得分:0)

这是我设法重现Hans Passant答案中行为的示例代码。我可以单击按钮1来创建一个对象,然后当我在创建线程终止后单击按钮2来访问它时,我得到错误“已经与其底层RCW分离的COM对象无法使用。” / p>

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