dotnet/wpf · error · InvalidOperationException

SR.CantShowOnDifferentThread

Error message

SR.CantShowOnDifferentThread

What it means

CommonDialog.CheckPermissionsToShowDialog verifies the calling thread matches the thread that constructed the dialog (_thread captured in the constructor). Showing the dialog from a different thread is blocked to mitigate multi-threaded attacks and because the dialogs are not thread-safe.

Solutions

  1. Create and show the dialog on the same thread, ideally the UI thread
  2. If on a background thread, marshal back to the dispatcher that owns the dialog: Application.Current.Dispatcher.Invoke(() => dlg.ShowDialog())
  3. Create a fresh dialog instance on the thread where it will be shown instead of reusing a cached one
  4. Avoid showing modal UI from worker threads entirely; collect input on the UI thread first

Example fix

// before
var dlg = new OpenFileDialog();
Task.Run(() => dlg.ShowDialog());
// after
var dlg = new OpenFileDialog();
Application.Current.Dispatcher.Invoke(() => dlg.ShowDialog());
Defensive patterns

Strategy: validation

Validate before calling

if (dlg is CommonDialog && dlg.CheckAccess() == false) // conceptually: ensure same thread
    Application.Current.Dispatcher.Invoke(() => dlg.ShowDialog());

Type guard

bool OnCorrectThread(CommonDialog d) => Thread.CurrentThread == d.GetType()
    .GetField("_thread", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(d);

Try / catch

try { dlg.ShowDialog(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("thread"))
{
    Application.Current.Dispatcher.Invoke(() => dlg.ShowDialog());
}

Prevention

When it happens

Trigger: Creating a Win32 OpenFileDialog/SaveFileDialog on thread A (e.g. the UI thread) and calling ShowDialog from thread B (e.g. a background Task or new thread), or caching a dialog instance and reusing it from another worker thread.

Common situations: Showing a file dialog from an async continuation that hopped to the thread pool; calling dialog.ShowDialog inside Task.Run; reusing a shared dialog field from a non-UI thread.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/Microsoft/Win32/CommonDialog.cs:232

            }
            return IntPtr.Zero;
        }

        /// <summary>
        ///  When overridden in a derived class, displays a particular type of common dialog box.
        /// </summary>
        protected abstract bool RunDialog(IntPtr hwndOwner);

        /// <summary>
        ///  Demands permissions appropriate to the dialog to be shown.
        /// </summary>
        protected virtual void CheckPermissionsToShowDialog()
        {
            // Verify we're on the right thread.  
            // This mitigates multi-threaded attacks without having to make the file dialogs thread-safe.
            if (_thread != Thread.CurrentThread)
            {
                throw new InvalidOperationException(SR.CantShowOnDifferentThread);
            }

        }

        #endregion Protected Methods

        //---------------------------------------------------
        //
        // Internal Methods
        //
        //---------------------------------------------------
        #region Internal Methods

        // This method is not used by IFileDialog API. Kept for compatibility (see HookProc).
        /// <summary>
        ///  Centers the given window on the screen. This method is used by HookProc
        ///  to center the dialog on the screen before it is shown.
        /// </summary>

View on GitHub (pinned to 81131a70a4)