Search code examples
c#.netvb.netcomcom-interop

Does a COM object get released by the CLR when the thread that created it terminates?


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?


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.