Fix RPC_E_WRONG_THREAD (0x8001010E) in COM Apps
This COM marshaling error means your app is trying to use an interface pointer on the wrong thread. Here's how to fix it for good.
Quick answer: The interface pointer was created on one apartment (usually STA) and you're calling it from another thread without proper marshaling. Marshal the interface via CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream, or make sure your calling thread calls CoInitializeEx with the same apartment model as the object.
I've seen this error pop up in all sorts of places – custom C# apps talking to legacy COM objects, Excel macros hitting a COM server, even a client's inventory scanner that kept crashing every Tuesday at 10 AM. The root is always the same: COM's threading rules are strict. An interface pointer marked for one apartment (think of an apartment as a thread's COM personality) can't just be handed to another thread. If you try, you get 0x8001010E. The fix is either marshaling or making sure the calling thread's apartment matches the object's expectations.
Why This Happens
When you create a COM object, it lives in a specific apartment – either Single-Threaded Apartment (STA) or Multi-Threaded Apartment (MTA). STA objects assume they're only called from their creating thread. MTA objects are more relaxed but still have rules. If you grab a pointer from an STA object and pass it to a worker thread without marshaling, COM throws this error to protect you from a crash or data corruption. Had a client last month whose entire print queue died because their app's UI thread (STA) passed an interface to a background thread without any marshaling. The fix took 15 minutes once we knew what to look for.
Fix Steps
- Identify the thread affinity. Check if the COM object is STA. Most UI-related COM objects (like those from Office, ActiveX controls, or shell extensions) are STA. You can tell by looking at the object's threading model in its registry:
HKEY_CLASSES_ROOT\CLSID\{your-clsid}\InprocServer32\ThreadingModel. If it'sApartmentor missing (defaults to STA), you're dealing with STA. - If you're on a worker thread, marshal the interface. On the thread that created the object (usually the UI thread), call
CoMarshalInterThreadInterfaceInStreamto serialize the interface pointer into a stream. Then pass that stream to the worker thread. On the worker thread, callCoGetInterfaceAndReleaseStreamto get a marshaled pointer you can safely use. - Alternatively, use the Global Interface Table (GIT). Call
CoCreateInstancewithCLSID_StdGlobalInterfaceTableto get a GIT object. On the original thread, callGIT::RegisterInterfaceInGlobal. On the worker thread, callGIT::GetInterfaceFromGlobal. This is cleaner for multiple threads. - If you control the COM object, mark it as Free-threaded. In your IDL file, set the threading model to
BothorFree. Then implement proper locking. This avoids marshaling entirely but requires thread-safe code. - Make sure the calling thread calls CoInitializeEx. Each thread that touches COM must call
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)if it's STA, orCOINIT_MULTITHREADEDfor MTA. If the thread doesn't initialize COM, you'll get odd errors.
Alternative Fixes
- Use a message pump on the STA thread. If your worker thread actually belongs to an STA (like a UI thread), make sure it's pumping messages. Otherwise COM can't marshal calls correctly. Add
Application.DoEvents()or a message loop. - Switch to a single-threaded architecture. Sometimes the easiest fix is to do all COM work on the main thread. Use
Control.Invokein .NET orPostMessagein C++ to marshal calls back to the creating thread. - Check for accidental cross-thread calls from timers or async events. Had a case where a
System.Timers.Timercallback in .NET was hitting a COM object created on the UI thread. The fix was to useSystem.Windows.Forms.Timerwhich runs on the UI thread.
Prevention Tip
Design your app so that COM objects are created and used on the same thread. If you need background threads, use the GIT from the start. Document the threading model of each COM object in your code comments. And test with multiple threads early – don't wait until production to discover this error.
Was this solution helpful?