The COINIT - an enumeration used to specify whether a windows thread is in a single or multithreaded apartment - documentation (http://msdn.microsoft.com/en-gb/library/windows/desktop/ms678505(v=vs.85).aspx) states that:
The multi-threaded apartment is intended for use by non-GUI threads. Threads in multi-threaded apartments should not perform UI actions. This is because UI threads require a message pump, and COM does not pump messages for threads in a multi-threaded apartment.
Why shouldn't threads in multi threaded apartments perform UI actions? What is wrong with having a message loop in a thread in a multi threaded apartment? Does COM somehow provide an automatic message loop for a thread in a single threaded apartment?
That's a bit backwards, a UI thread primarily requires a message loop so that it can receive notifications from Windows and other processes. A message loop is the universal solution to the producer-consumer problem. With the operating system and other processes producing, the UI thread consuming.
A UI thread uses lots and lots of code that is not thread-safe. This includes major features implemented in COM, like drag+drop, the clipboard, the shell dialogs, ActiveX controls like a browser. And a raft of code that was never made thread-safe because the programmer didn't have to make it so, much easier to write. Those features require an STA thread, in other words a thread that initializes COM by passing COINIT_APARTMENTTHREADED to CoInitializeEx().
That is a promise to COM that the thread will be a good citizen, it is not allowed to make blocking calls and must pump a message loop. It is the message loop that COM uses to marshal a call from a worker thread to the STA thread in order to keep a COM object thread-safe. When all the calls are made from the same thread then there's never a safety issue. The underlying call is SendMessage(), with a ton of plumbing to copy the function arguments from one stack to another. CoInitializeEx() creates a hidden message window owned by the STA thread that processes the message and actually makes the call. Safely.
Related
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?
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:
The class implementing IFileOperation is not thread safe
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
I am reviewing some code where MTA threads are calling making a call to EnumThreadWindows with their thread ID and looking for a Window with a particular class name. This window is created by an STA COM object explicitly for the purpose of tying an MTA thread to a itself.
I'm sure this works great when there's a single STA object (and therefore only one window); however, in the scenario where there are 8 of these windows and because these are non-GUI MTA threads I am wondering how can these threads be associated with a specific window. We don't create these, they're part of a thread pool in Internet Explorer relating to asynchronous pluggable protocol handling.
Unless I'm missing some 'magic' I would think that the EnumThreadWindows call would result in all windows in the process being enumerated because the threads are non-GUI threads.
I've done some initial exploration of this; however, the scenario in which these operations happen in our code is fairly complicated and doesn't lend it self well to this.
Is the enumeration behavior of EnumThreadWindows explained in more detail than what we see in MSDN?
I have a thread which creates COM objects that use the STA model.
This thread's Run function puts it in an infinite WaitForMultipleObjects.
Is it possible that the infinite WaitForMultipleObjects could prevent other threads from marshaling calls to the COM objects owned by this thread?
Basically, I'm asking if WaitForMultipleObjects would prevent the hidden COM message queue from being pumped.
Yes, problems are possible - see this KB article. Basically if your thread is an STA thread it should not call functions that can block for long periods of time since while the thread is blocked it doesn't pump and dispatch messages and this can prevent proper marshalling functioning.
If I call the same COM function from multiple threads to an in proc COM Dll, how thread safe is that?
Do all my objects in the COM DLL also need to be thread safe for this to work reliably?
COM takes care of threading on behalf of the COM server. The server publishes the kind of threading it supports with the ThreadingModel registry key. Very common settings are Apartment or Both. Free is very rare. A missing key is equivalent to Apartment.
COM requires a single-threaded apartment (STA) for apartment threaded servers. If you don't provide one (CoInitialize/Ex call) then it will create a dedicated thread for the server. A hard requirement for an STA thread is that it also pumps a Windows message loop. The message loop is the mechanism by which COM automatically marshals a method call from one thread to another.
So, the general answer to your question is, yes, it normally is thread-safe. There are still things that can go wrong. Deadlock is possible when a call is made from a worker thread but the STA thread isn't pumping. Or the server could be fibbing about the ThreadingModel it registered. Not uncommon with servers implemented in .NET. They get registered as Both, but there are few .NET classes that are actually thread-safe.
See this very detaled article. Basically COM will take care of synchronization - you don't need to bother. However in certain cases the consumer can experience significant slowdown because of synchronization.
Depends upon the COM objects threading model. If its free threaded then you are responsible for thread safety. If its in a single threaded apartment then you can only call it from one, and if it's in a multithreaded apartment, then you can can but as always you have to consider the implications for the object's state. there is a very good answer on SO Could you explain STA and MTA? explaining this.
Following up from part of this question, what is the best way to have a worker thread that invokes a COM procedure in a DLL, to do this asynchronously so that the worker thread is not blocked on this call?
Note that a call to this COM DLL can take a long time as it will do DB accesses and possible run long running queries.
I wondered if asynch is possible in this scenario or if one has to just wait for the call to complete?
Take a look at this article:
http://msdn.microsoft.com/en-us/magazine/cc301334.aspx
You can also do it by yourself: create another thread for COM calls, and pass information from your worker thread to it somehow - for instance, using a queue of command pattern objects.
http://en.wikipedia.org/wiki/Command_pattern
This is, in general, not possible. COM takes care of the threading requirements for a COM server. Which it advertises in the registry. Have a look-see with Regedit.exe and locate the HKCR\CLSID{guid} key for the coclass that you use. The ThreadingModel key says what is required.
If it is missing or set to Apartment then the server says that it needs to be created on an STA thread and its interface methods must be called from that same thread. If you call a method from another thread then the call gets marshaled to the STA thread. That's safe but it is slow and you don't have any concurrency.
If it is set to Both then the call is still marshaled when the COM object was created on an STA thread. But not when it was created on an MTA thread. Only Free allows free threading. Which is very rare, the vast majority of COM components are apartment threaded and don't have the internal protection that's required to make calls from a worker thread.
There's typically only one STA thread in a program, the startup thread. It must pump a message loop, a hard requirement for STA. If you don't have one, COM will create an STA thread for you to find a good home for the server. All calls will be marshaled then.
Not good news, I'm sure, there's no free lunch in threading.