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
- Create and show the dialog on the same thread, ideally the UI thread
- If on a background thread, marshal back to the dispatcher that owns the dialog: Application.Current.Dispatcher.Invoke(() => dlg.ShowDialog())
- Create a fresh dialog instance on the thread where it will be shown instead of reusing a cached one
- 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
- Always create and show dialogs on the UI thread
- Never call ShowDialog inside Task.Run or raw Thread.Start
- Marshal to the owning Dispatcher when calling from background work
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
- ElementNotAvailableException
- Processing is disabled while the Dispatcher is in this…
- SR.CalendarCollection_MultiThreadedCollectionChangeNotSuppor…
- SR.CalendarCollection_MultiThreadedCollectionChangeNotSuppor…
- SR.CollectionView_MissingSynchronizationCallback
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)