dotnet/wpf · error · ThreadStateException

SR.OleServicesContext_ThreadMustBeSTA

Error message

SR.OleServicesContext_ThreadMustBeSTA

What it means

OleServicesContext's DoDragDrop path requires an STA (single-threaded apartment) thread because OLE drag-drop COM interfaces must run on an STA. If the current thread's apartment state is not STA, it throws ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA) before invoking OLE.

Solutions

  1. Initiate drag-drop on the UI thread (Dispatcher.Invoke/BeginInvoke to marshal to the STA main thread).
  2. If creating the thread yourself, call thread.SetApartmentState(ApartmentState.STA) before Thread.Start().
  3. Ensure [STAThread] is present on the application's Main entry point so the main thread is STA.

Example fix

// before
Task.Run(() => DragDrop.DoDragDrop(source, data, DragDropEffects.Copy)); // MTA thread -> throws
// after
Dispatcher.BeginInvoke(() => DragDrop.DoDragDrop(source, data, DragDropEffects.Copy));
Defensive patterns

Strategy: validation

Validate before calling

if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
    throw new InvalidOperationException("DoDragDrop must run on an STA thread"); // guard before calling

Type guard

static bool IsStaThread() => Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;

Try / catch

try { DragDrop.DoDragDrop(depObj, dataObj, effects); }
catch (ThreadStateException ex) when (ex.Message.Contains("STA")) { /* marshal to UI thread and retry */ }

Prevention

When it happens

Trigger: Calling DragDrop.DoDragDrop (via OleServicesContext.OleDoDragDrop) from a non-STA thread, e.g. a worker/MTA thread that initiates a drag operation.

Common situations: Starting drag-drop from a Task.Run/background thread, console or service code touching WPF drag APIs, or a thread created with default MTA state (Thread.SetApartmentState never called).

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/c19768e82e8a98da. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/OleServicesContext.cs:91

    /// </summary>
    internal static void EnsureThreadState()
    {
        // Poke the CurrentOleServicesContext to ensure it is created, this will throw if the current thread is not STA.
        _ = CurrentOleServicesContext;
    }

    /// <summary>
    ///  Call OLE Interop DoDragDrop().  Initiate OLE DragDrop.
    /// </summary>
    internal void OleDoDragDrop(
        IComDataObject dataObject,
        UnsafeNativeMethods.IOleDropSource dropSource,
        int allowedEffects,
        int[] finalEffect)
    {
        if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
        {
            throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
        }

        InputManager inputManager = (InputManager)Dispatcher.CurrentDispatcher.InputManager;
        inputManager?.InDragDrop = true;

        try
        {
            UnsafeNativeMethods.DoDragDrop(dataObject, dropSource, allowedEffects, finalEffect);
        }
        finally
        {
            inputManager?.InDragDrop = false;
        }
    }

    /// <summary>
    ///  Call OLE Interop RegisterDragDrop()
    /// </summary>

View on GitHub (pinned to 81131a70a4)