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

  1. Create the control on an STA thread: mark the entry point with [STAThread] or call Thread.SetApartmentState(ApartmentState.STA) before Thread.Start.
  2. If using a background thread, create a dedicated Thread (not ThreadPool/Task) and set STA before instantiating the control.
  3. 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

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


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 Methods

View on GitHub (pinned to 81131a70a4)