Question

I have been unable to figure out how to search for confirmation on this suspicion, but I am seeing evidence that a COM object created on one thread is no longer available to other threads (COM object that has been separated from its underlying RCW cannot be used) once code stops executing on the thread that created it (and that thread may have terminated). This is a really insidious problem to track down because I have calls to System.Runtime.InteropServices.Marshal.ReleaseComObject throughout my code, but I could not identify any of them were being called causing this error. Finally I came to the conclusion that the COM object was apparently being implicitly freed when the secondary thread stopped executing. Could this be true? Is this documented behavior?

Was it helpful?

Solution

Yes, a COM object tends to have strong thread affinity. Threading is not a minor implementation detail in COM. Unlike .NET, COM provides thread-safety guarantees for a COM class. COM can publish the kind of threading it supports, with "apartment" (i.e. "not thread-safe") a very common choice. COM ensures that these requirements are met without the program having to do anything to help. Marshaling a call from one thread to another so that the object is always used in a thread-safe way is automatic. In .NET code you normally have to do this yourself, using Control.BeginInvoke or Dispatcher.BeginInvoke, for example.

An automatic consequence of this is that a thread that owns one or more COM objects that's allowed to exit will automatically get these objects released. This is necessary since there is no longer a way to meet the thread safety requirement. Trying to use them anyway after this is going to bomb. There is no cure for this beyond ensuring that the thread stays alive long enough to keep servicing these objects. Similarly, you'd need to keep the UI thread alive long enough to ensure that Dispatcher.BeginInvoke can still work in .NET.

Fwiw, yes, using Marshal.ReleaseComObject() can give you plenty of fud about this. Explicit memory management has a long history of producing buggy programs, and automatic garbage collection provided the cure. GC is quite capable of getting that COM object released without your help and never gets it wrong. It just takes a bit longer to get around to it. If you know that the COM object has unusually high resource usage that warrants releasing it deterministically then you do the exact same thing you'd do for a .NET object graph that's expensive: GC.Collect() helps that along. Check this answer for the reason why Marshal.ReleaseComObject() tends to be used unnecessarily.

OTHER TIPS

Here's sample code with which I managed to reproduce the behavior in Hans Passant's answer. I can click on button 1 to create an object, and then when I click on button 2 to access it after the creating thread has terminated, I get the error "COM object that has been separated from its underlying RCW cannot be used."

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top