Search code examples
c#com-interopstamta

An MTA Console application calling an STA COM object from multiple threads


Although there are many questions about COM and STA/MTA (e.g. here), most of them talk about applications which have a UI. I, however, have the following setup:

  • A console application, which is by default Multi-Threaded Apartment (Main() explicitly has the [MTAThread] attribute).
  • The main thread spawns some worker threads.
  • The main thread instantiates a single-threaded COM object.
  • The main thread calls Console.ReadLine() until the user hits 'q', after which the application terminates.

A few questions:

  • Numerous places mentions the need of a message pump for COM objects. Do I need to manually create a message-pump for the main thread, or will the CLR create it for me on a new STA thread, as this question suggests?
  • Just to make sure - assuming the CLR automagically creates the necessary plumbing, can I then use the COM object from any worker thread without the need of explicit synchronization?
  • Which of the following is better in terms of performance:
    • Let the CLR take care of the marshaling to and from the COM object.
    • Explicitly instantiate the object on a separate STA thread, and have other thread communicate with it via e.g. a ConcurrentQueue.

Solution

  • Yes, it is possible to create a STA COM object from an MTA thread.

    In this case, COM (not CLR) will create an implicit STA apartment (a separate COM-owned thread) or re-use the existing one, created ealier. The COM object will be instantiated there, then a thread-safe proxy object (COM marshalling wrapper) will be created for it and returned to the MTA thread. All calls to the object made on the MTA thread will be marshalled by COM to that implicit STA apartment.

    This scenario is usually undesirable. It has a lot of shortcomings and may simply not work as expected, if COM is unable to marshal some interfaces of the object. Check this question for more details. Besides, the message pump loop, run by the implicit STA apartment, pumps only a limited number of COM-specific messages. That may also affect the functionality of the COM.

    You may try it and it may work well for you. Or, you may run into some unpleasant issues like deadlocks, quite difficult to diagnose.

    Here is a closely related question I just recently answered:

    StaTaskScheduler and STA thread message pumping

    I'd personally prefer to manually control the logic of the inter-thread calls and thread affinity, with something like ThreadAffinityTaskScheduler proposed in my answer.

    You may also want to read this: INFO: Descriptions and Workings of OLE Threading Models, highly recommended.