CLR SPY and Customer Debug Probes: The Thread Changing Apartment State Probe

The last blog touched on apartment states of managed threads, which the CLR tracks for COM interoperability purposes.  But if an application changes the apartment state of a thread being tracked by the CLR (via unmanaged code or direct calls to CoUninitialize/CoInitialize), the runtime won't be aware of this change.  Its information for that thread would become stale, and the CLR may not behave correctly as a result.  The Thread Changing Apartment State probe (or simply the "Apartment probe") helps the CLR detect and report when this situation occurs.

Whenever the CLR bases a decision on the apartment state of a thread, this probe makes the runtime ask COM what the thread's current apartment state really is, and then compare it to the apartment state the CLR had recorded it to be.  If there's a difference, you'll get a message like the following:

  Thread (0x658) used to be in the MTA, but the application has CoUninitialized and the thread is now in a STA.

The hexadecimal number is a thread ID, matching what you would see in a debugger.  Note that even with this additional information, the CLR still won't correct its cached apartment state information to match what COM says the apartment state is.  That way, the application exhibits the same run-time behavior you'd get without the probe enabled.

This probe checks for another class of problems, too: situations in which the CLR knows the correct apartment state, but the developer might not.  Whenever the CLR attempts to set the apartment state of a thread that was already initialized to a different state, this probe makes it warn you that the attempt didn't succeed.  In v1.1, this type of warning doesn't appear in every situation that you might expect it to, but here's a simple C# example that always provokes the message:

  Thread t = new Thread(new ThreadStart(SomeMethod));
t.ApartmentState = ApartmentState.MTA;
t.ApartmentState = ApartmentState.STA; // This does nothing.

The message we get is:

  Unstarted thread is trying to set the apartment state to STA, but it has already been set to MTA.

The example from the previous blog does not produce any output from this probe, but if you add the following line right after the printf (and also use the System::Threading namespace):

  Thread::CurrentThread->ApartmentState = ApartmentState::MTA;

You get the following message:

  Thread (0xc54) is trying to set the apartment state to MTA, but it has already been set to STA.

Note that in v1.1 all unmanaged hosts that instantiate managed components via COM Interop on an STA thread will cause this probe to emit this sort of warning.  That's because the CLR tries to initialize the thread to MTA and doesn't care if it fails.  This is an extra bit of noise that affects applications like Visual Studio .NET the first time it loads the CLR (typically when you open a new managed project) or Windows Media Player when running my favorite managed visualization.