dotnet/wpf · error · ThreadStateException
SR.Format(SR.AxRequiresApartmentThread, clsid.ToString())
Error message
SR.Format(SR.AxRequiresApartmentThread, clsid.ToString())
What it means
ActiveXHost (base of WindowsFormsHost-style interop controls) requires an STA thread because ActiveX controls must run in a single-threaded apartment. The constructor checks Thread.CurrentThread.GetApartmentState() and throws ThreadStateException if the hosting thread is not STA, including the control's CLSID in the message.
Solutions
- Create the control on an STA thread: mark the entry point with [STAThread] or call Thread.SetApartmentState(ApartmentState.STA) before Thread.Start.
- If using a background thread, create a dedicated Thread (not ThreadPool/Task) and set STA before instantiating the control.
- Use a dispatcher/SynchronizationContext to marshal control creation onto the existing UI (STA) thread.
Example fix
// before var thread = new Thread(() => new ActiveXControl(clsid)); thread.Start(); // after var thread = new Thread(() => new ActiveXControl(clsid)); thread.SetApartmentState(ApartmentState.STA); thread.Start();
Defensive patterns
Strategy: validation
Validate before calling
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
throw new InvalidOperationException("Control must be created on an STA thread");
// create control here Type guard
bool CanCreateActiveXHost(Thread t) =>
t.GetApartmentState() == ApartmentState.STA; Try / catch
try
{
var control = new ActiveXControl(clsid);
}
catch (ThreadStateException ex)
{
// recreate on a dedicated STA thread or marshal to UI thread
Log(ex);
} Prevention
- Always mark UI entry points with [STAThread].
- Never create interop controls on ThreadPool/Task threads; use a dedicated STA Thread.
- Assert apartment state in test setup for UI/interop tests.
When it happens
Trigger: Constructing an ActiveXHost-derived control (or a wrapped COM control like WebBrowser/WindowsFormsHost scenarios) on an MTA thread or a thread whose apartment state was never set (MTA by default on worker threads).
Common situations: Creating WPF/WinForms interop controls on background Task/ThreadPool threads, console apps that never set [STAThread] on Main, or COM server/test harnesses spinning plain threads to instantiate controls.
Related errors
- SR.PTProvider_VerifyAccess
- SR.RequiresSTA
- SR.RequiresSTA
- SR.Verify_ApartmentState (template: Verify_ApartmentState…
- ElementNotAvailableException
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/5fe36feb76f5129a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Interop/ActiveXHost.cs:85
EventManager.RegisterClassHandler(typeof(ActiveXHost), AccessKeyManager.AccessKeyPressedEvent, new AccessKeyPressedEventHandler(OnAccessKeyPressed));
Control.IsTabStopProperty.OverrideMetadata(typeof(ActiveXHost), new FrameworkPropertyMetadata(true));
FocusableProperty.OverrideMetadata(typeof(ActiveXHost), new FrameworkPropertyMetadata(true));
EventManager.RegisterClassHandler(typeof(ActiveXHost), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnGotFocus));
EventManager.RegisterClassHandler(typeof(ActiveXHost), Keyboard.LostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnLostFocus));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(ActiveXHost), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
}
/// constructor for ActiveXHost
internal ActiveXHost(Guid clsid, bool fTrusted ) : base( fTrusted )
{
// What if the control is marked as free-threaded?
if (Thread.CurrentThread.GetApartmentState() is not ApartmentState.STA)
{
throw new ThreadStateException(SR.Format(SR.AxRequiresApartmentThread, clsid.ToString()));
}
_clsid = clsid;
// hookup so we are notified when loading is finished.
Initialized += new EventHandler(OnInitialized);
}
#endregion Constructors and Finalizers
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected MethodsView on GitHub (pinned to 81131a70a4)