3

This thread is great at explaining STA vs MTA for COM, however it doesn't address how to code for such or when to use one or the other, and only just discusses technicalities about COM apartments being used by thread-safe objects or not. I'd be willing to bet most users just want to know how to use the Win API through COM without any COM objects shared among multiple threads.

Should you use STA always if your COM objects aren't shared among threads, and your code makes use of multiple threads or a single thread each with its own COM object instances, none of the objects shared? Does it depend on the object you are using? If you don't always use STA for such when do you use MTA? Do you ever need a message pump in this case?

In my case I use the Task Scheduler API (ITaskService) and the Shell Links/Create Shortcut API (IShellLink) from the main GUI thread (using the Qt Framework), and the File Operation API (IFileOperation) and the Volume Shadow Copy Service API from a worker thread.

I call CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); from each thread before initializing and using the COM objects and CoUninitialize(); after. Is this proper usage of COM? Would the same apply using COM objects from the main GUI thread without a worker thread?

riverofwind
  • 525
  • 4
  • 17
  • In an ideal world, as a thread owner, you shouldn't care (put aside possible performance issues for cross apartment calls) if the COM servers you use are STA or MTA. That's basically the whole point of all this COM threading stuff, it's supposed to be a *feature* not a burden... Unfortunately, most shell object (and others...) just don't provide the necessary objects (tlb, proxies, etc.) to marshal calls between apartments (exact reasons are still obscure to me, beyond compatibility issues for Microsoft...). So you must care... For these objects, shared or not, you *must* create an STA thread. – Simon Mourier Sep 10 '19 at 07:14
  • Selecting STA is a promise, cross your heart hope to die. Never block and pump a message loop, you know when you are going to do that. You want the okay to lie about it and hope to die when the lie turns out to cause trouble. That usually turns out okay, your program will deadlock or an event you expect to get raised doesn't trigger. No guarantee, and not easy to diagnose, it is the way they quickly get you off the phone when you call Microsoft Support. Or SO. If you are going to lie about shell interfaces then you also need to test on Win7. – Hans Passant Sep 10 '19 at 08:00
  • @Simon So is it safe to always use STA? – riverofwind Sep 11 '19 at 20:02
  • If you're using Shell object, it's probably the safest way. But nothing's ever guaranteed. – Simon Mourier Sep 11 '19 at 20:39

1 Answers1

1

For making outbound COM calls to objects that you instantiated via CoCreateInstance, STA should be good enough, and is almost a must for your GUI thread (the one that has a GetMessage/DispatchMessage loop).

MTA starts to become relevant when hosting your own thread safe COM objects that are expected to be invoked from other processes.

The documentation for IFileOperation states this:

IFileOperation can only be applied in a single-threaded apartment (STA) situation. It cannot be used for a multithreaded apartment (MTA) situation. For MTA, you still must use SHFileOperation.

See all, this link: INFO: Calling Shell Functions and Interfaces from a Multithreaded Apartment

I suspect what the documentation is really trying to say is this:

  1. The class implementing IFileOperation is not thread safe
  2. It's ThreadingModel is declared "apartment" in the registry and will incur marhsalling overhead if accessed from an MTA thread.

On our application, have used ITaskScheduler on the main STA thread. And we use IFileOperation on a background STA thread that has its own message pump.

Some other links that I think are very useful:

https://support.microsoft.com/en-us/help/150777/info-descriptions-and-workings-of-ole-threading-models

https://devblogs.microsoft.com/oldnewthing/?p=22603

selbie
  • 100,020
  • 15
  • 103
  • 173
  • Thanks selbie. I don't have to manually create a message pump with GetMessage/DispatchMessage since I'm using Qt right? Also is it 100% safe to always use STA without COM objects shared among threads? Don't want any problems! :) – riverofwind Sep 11 '19 at 20:09
  • QT framework will "pump messages" for you on the main thread. So you are good there. I'm not sure I understand your last question. Can you elaborate on what you mean by "use STA without COM objects shared among threads" ? – selbie Sep 12 '19 at 01:04
  • I mean as long as I don't share my COM objects among my GUI and worker threads, leaving them isolated within each, is it 100% safe to just use STA when initializing COM on each thread? – riverofwind Sep 12 '19 at 21:00
  • Yes, I think you'll be fine for outbound COM calls. – selbie Sep 13 '19 at 02:06
  • Do I need to initialize COM on the main GUI thread before the worker thread so COM sees it as the GUI thread with the message pump? I vaguely remember reading somewhere about that. Or does the order not matter... – riverofwind Sep 13 '19 at 04:51
  • Yes. Invoke `CoInitialize(nullptr)` as the first line of WinMain. – selbie Sep 13 '19 at 06:21
  • Actually using old school main... same thing right? – riverofwind Sep 13 '19 at 23:48
  • Yes, main should be fine. – selbie Sep 13 '19 at 23:50
  • I take it that's all folks right? Nothing else relevant? – riverofwind Sep 15 '19 at 19:37
  • Just for you to accept this answer and give it the green check. – selbie Sep 16 '19 at 03:03
  • Also, final comment. If you have multiple STA threads, your main STA thread may still need to pump messages in order for CoCreateInstance to succeed on those other threads. You'll know it when you hit a deadlock. – selbie Sep 16 '19 at 16:48