For the uninformed, COM objects can be accessed in two different ways: single threaded apartment and multithreaded apartment. Many components are designed to work in only one mode or the other, such as several Winforms GUI components. Because of this, the default program generated by Visual Studio attaches the [STAThread] attribute to tell the runtime to initialize the main thread as using single threaded apartment mode.
So far, so good. I'm writing a bit of managed glue code in C++/CLI that uses a COM component that requires single threaded apartment mode to run correctly. I check to make sure that my test application has the appropriate thread set, and then run my first test, with good results. Awash with the euphoria of victory, I walk away to go eat a brownie.
When I return, I make a few adjustments to my DLL, and run the test app again. Lo and behold, I fail with a cryptic HRESULT return code from CoCreateInstance, which is in charge of creating the necessary COM component. Frowning, I rollback my changes and try again. Still no good. This makes no sense, right?
After a lot of digging, I do a little test. I insert the following code right before CoCreateInstance:
ApartmentState state = Thread::CurrentThread->GetApartmentState();
I put a breakpoint there and run the debugger. Sure enough, the apartment state for thread is set to MTA (multithreaded apartment). That's odd. I'm sure it's set to STA. I check back to my Main method to be sure. Yep, it's still got the attribute on there. I insert my debugging code as the first line of Main. You can see the hilariousness for yourself here:
So apparently, the CLR is ignoring my STAThread attribute and initializing the main thread as multithreaded. Not very considerate if you ask me. On an off chance, I try running my application without the debugger. Success! We now have an STA thread. I try a few other configurations, each with mixed results.
I did a little Googling, and while there is some confirmation of this peculiar behavior, there was little on how to actually fix it. I fought with it for a bit before giving in and adding the following lines before the creation of the COM component:
if (Thread::CurrentThread->GetApartmentState() != ApartmentState::STA){ CoUninitialize(); CoInitialize(NULL);}
Compile and run without issues! It's a little heavy handed I'll admit, but I'm not really sure what else to do. Has anyone else ever experienced this behavior? It's really quite bizarre. Leave a comment below to let me know what you think!