See the question and my original answer on StackOverflow

Your EXE server defines this in stdafx.h (probably added by VS wizards):

#define _ATL_APARTMENT_THREADED
  • So it means the exe server will initialize COM using CoInitialize(NULL), which means the thread that will create the .NET object will be STA.
  • .NET objects are by default marked "both", so they basically can live in STA or in MTA. So your .NET object will be STA.
  • The DomDocument needs an STA.
  • The free-threaded DomDocument needs an MTA. (it's not "both').

As a rule of thumb, an STA thread means COM messages (=method calls) for objects created in this thread are pumped by a COM-created message loop running in this thread. If you run your own loop in that thread and never lets the COM message loop run, then no method calls will ever get to objects in this STA.

STA is a feature (often enabled by default w/o anyone noticed) that will make sure an object marked as such will not be called by random threads and crash.

In your loop, when you create STA instances, you implicitely let the COM loop run, so method calls (Stop) can work it through. When you create FT DomDocument instances, the COM loop simply doesn't run and no method call get through.

You'll have to understand how COM threading works to make the best decision in your case.

With your sample, you can just change your exe code and comment _ATL_APARTMENT_THREADED (the default is _ATL_FREE_THREADED) In this case, everything will run in MTA (well, if you still use a non FT DomDocument, it will live on an STA).

note: CoInitialize(Ex) must be called by the thread owner. Once called, it's useless to call it again. You code has some useless CoInitializeEx around.