.NET Thread Apartment and COM Interop

Before a thread can call into a COM object it has to define its apartment by declaring whether it will enter a single-threaded apartment (STA) or a multi-threaded apartment (MTA). STA client threads call CoInitialize(NULL) or CoInitializeEx(0, COINIT_APARTMENTTHREADED) to enter an STA apartment and MTA threads call CoInitializeEx(0, COINIT_MULTITHREADED) to enter an MTA.

When calling a COM component, it is important to ensure that the threading models of the component and the client are compatible. For instance VB6 components insist on using the STA threading model. With an STA component, a message queue is associated with a hidden window and requests are queued and a single thread in STA apartment processes the messages synchronously of the queue. This ensures that access to the component is synchronised. However, if the client uses an MTA threading model to instantiate this STA component, not only a thread switch between the MTA and the STA thread needs to occur but also an apartment marshalling is required which can be an expensive process. Therefore it is recommended to use compatible threading models to ensure that apartment threaded component instance is created in the client's apartment.

Similarly, in the .NET managed world, you have the option of allowing the calling thread in the managed space declare its apartment affinity. For example, it is possible to associate a thread in .NET to the single-threaded apartment. This is usually the recommended approach when calling legacy VB6 COM.

Threads inside .NET’s thread pool are associated to the multi-threaded apartment (MTA) therefore requiring a thread switch and an apartment marshalling when calling VB6 components. It is recommended to use dedicated threads with ApartmentState property set to STA when calling into an STA component (use SetApartmentState method).