Why does CoCreateInstance work even though my thread never called CoInitialize? The curse of the implicit MTA


While developing tests, a developer observed erratic behavior with respect to Co­Create­Instance:

In my test, I call Co­Create­Instance and it fails with CO_E_NOT­INITIALIZED. Fair enough, because my test forgot to call Co­Initialize.

But then I went and checked the production code: In response to a client request, the production code creates a brand new thread to service the request. The brand new thread does not call Co­Initialize, yet its call to Co­Create­Instance succeeds. How is that possible? I would expect the production code to also get a CO_E_NOT­INITIALIZED error.

I was able to debug this psychically, but only because I knew about the implicit MTA.

The implicit MTA is not something I can find very much documentation on, except in the documentation for the APP­TYPE­QUALIFIER enumeration, where it mentions:

[The APT­TYPE­QUALIFIER_IMPLICIT_MTA] qualifier is only valid when the pAptType parameter of the Co­Get­Apartment­Type function specifies APT­TYPE_MTA on return. A thread has an implicit MTA apartment type if it does not initialize the COM apartment itself, and if another thread has already initialized the MTA in the process. This qualifier informs the API caller that the MTA of the thread is implicitly inherited from other threads and is not initialized directly.

Did you get that? If any thread in the process calls Co­Initialize­[Ex] with the COINIT_MULTI­THREADED flag, then that not only initializes the current thread as a member of the multi-threaded apartment, but it also says, "Any thread which has never called Co­Initialize­[Ex] is also part of the multi-threaded apartment."

Further investigation revealed that yes, some other thread in the process called Co­Initialize­Ex(0, COINIT_MULTI­THREADED), which means that the thread which forgot to call Co­Initialize was implicitly (and probably unwittingly) placed in the MTA.

The danger of this implicit MTA, of course, is that since you didn't know you were getting it, you also don't know if you're going to lose it. If that other thread which called Co­Initialize­Ex(0, COINIT_MULTI­THREADED) finally gets around to calling Co­Un­initialize, then it will tear down the MTA, and your thread will have the MTA rug ripped out from under it.

Moral of the story: If you want the MTA, make sure you ask for it explicitly. And if you forget, you may end up in the implicit MTA, whether you wanted it or not. (Therefore, conversely, if you don't want the MTA, make sure to deny it explicitly!)

Exercise: Use your psychic debugging skills to diagnose the following problem. "When my code calls Get­Open­File­Name, it behaves erratically. I saw a Knowledge Base article that says that this can happen if I initialize my thread in the multi-threaded apartment, but my thread does not do that."

Comments (12)
  1. Joshua says:

    Ewww.

    GetOpenFileName should work without the caller initializing COM. It happens to be invokable from higher languages that cannot invoke COM.

    Picking up the implicit MTA in that case is very bad. Worse, if it came from an AppInit DLL.

  2. RPC_E_CHANGED_MODE says:

    I'm pretty sure you can't initialise apartment threaded once you've initialised (or been initialised) multithreaded. Also, prepare for fun if your threads still call CoUninitialize after a failed CoInit.

  3. Peter says:

    Raymond, does it matter when CoInitialize is called in relation to thread start?  That is, if thread 1 calls CoInitializeEx(COINIT_MULTITHREADED) and thread 2 has already started, does thread 2 suddenly become part of the multithreaded apartment?

  4. Gabe says:

    Peter: You can probably answer the question yourself by thinking about how it could possibly work.

    In order for the thread start order to matter, either starting a thread would have to execute some code check for an existing MTA and store that information somewhere, or the CoInitializeEx would have to store the time it created an MTA to be compared with the thread start time.

    In order for thread start order to not matter, the object creation code can simply check to see if there is currently an MTA.

    Hopefully the answer is obvious given the options.

  5. JM says:

    Well, your thread doesn't do that *explicitly*, but through the miracle of the implicit MTA created by another thread it's now part of an MTA nevertheless. To fix the problem, explicitly call CoInitializeEx() with COINIT_APARTMENTTHREADED on all threads that need to call shell functions, even if they do not otherwise use COM.

  6. @RPC_E_CHANGED_MODE:

    That is only true for previous calls to CoInitialize(Ex) (referred to as CoInit from now on).

    The implicit MTA would work along the lines of if no call to CoInit has taken place, then the creation of the first object would in essence call CoInit and mark the thread as part of the MTA. This is different from a thread that has never called CoInit or created any objects before calling CoInit to initialise the thread as an STA and failing. In the latter case, the CoInit would succeed and set the thread to an STA with no issues because COM has yet to be initialised on that thread. It really has to be this way around, otherwise there is potential for a lot of breakage, especially when using things like the common dialogs.

  7. Jonathan Wilson says:

    The fact that you need to even know about COM in order to properly use the common dialogs library (a library that has existed since before COM did) is stupid IMO.

  8. @Crescens2k says:

    Ah, of course: you're right. I've never explicitly mixed threading models in the same app, so forgot about the scenario. Thanks.

  9. Cheong says:

    For .NET related note: WinForm/Console Projects created under VS2002 or 2003 always have [STAThread] attribute predefined in main() method (even after conversion to .NET v2 or above, don't know whether the conversion ignored this attribute or someone added it back later).

    VS2005 or later have this removed from the default project template and use MTA as default.

  10. Mike Dimmick says:

    @cheong00: Windows Forms has thread affinity and must initialize the main thread in an STA. I've just checked: VS2008 (2.0 and 3.5), VS2010 (4.0) and VS2012 (4.5) C# Windows Forms projects all default to specifying [STAThread], as they should. I no longer have VS2005 installed.

    Console projects do not default to [STAThread] in any version, so far as I'm aware, though I haven't checked.

  11. Cheong says:

    I see. It makes sense.

    Not sure why the projects I'm working on removed this attribute, though.

    Btw, it's true that in .NET v2 or later it's defaulted to MTA if STAThread is not specified (from MSDN documentation for STAThreadAttribute).

  12. Stewart says:

    @Jonathan – You don't need to care about COM to use GetOpenFileName. If your app does not use COM at all, then GetOpenFileName will take care of things for you. The problem arises when your app already cares about COM, and has a thread which has already initialized the MTA. Then bad things will happen. Of course you may not know about this thread, which is where the risk comes in. In my opinion it is good practise for any thread which shows UI to always initialize itself an STA before creating any UI. That way you're safe since you should only be calling GetOpenFileName on a UI thread anyway. As has already been said, WinForms does this already in default apps.

    @Mike – Console projects cannot initialize to STAThread by default, because console applications do not typically pump messages on their main thread, and it is illegal to have an STA thread which does not pump messages. If they defaulted to STA they'd be broken.

Comments are closed.