{"record":{"id":"246bccc8b6d9f83a","repo":"stride3d/stride","slug":"this-method-must-be-invoked-from-the-dispatcher-thread","errorCode":null,"errorMessage":"This method must be invoked from the dispatcher thread","messagePattern":"This method must be invoked from the dispatcher thread","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/presentation/Stride.Core.Presentation.Wpf/Windows/WindowManager.cs","lineNumber":263,"sourceCode":"        {\n            var window = windowInfo.Window;\n            if (window == null)\n                return false;\n\n            var location = window.GetType().Assembly.Location;\n            if (string.IsNullOrEmpty(location))\n                return false; // dynamic/single-file assembly: can't tell, assume ours to avoid hiding real dialogs\n\n            return !location.StartsWith(AppContext.BaseDirectory, StringComparison.OrdinalIgnoreCase);\n        }\n\n        private static void CheckDispatcher()\n        {\n            if (dispatcher.Thread != Thread.CurrentThread)\n            {\n                const string message = \"This method must be invoked from the dispatcher thread\";\n                Logger.Error(message);\n                throw new InvalidOperationException(message);\n            }\n        }\n\n        private static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)\n        {\n            if (hwnd == IntPtr.Zero)\n                return;\n\n            var rootHwnd = NativeHelper.GetAncestor(hwnd, NativeHelper.GetAncestorFlags.GetRoot);\n            if (rootHwnd != IntPtr.Zero && rootHwnd != hwnd)\n            {\n                Logger.Debug($\"Discarding non-root window ({hwnd}) - root: ({NativeHelper.GetAncestor(hwnd, NativeHelper.GetAncestorFlags.GetRoot)})\");\n                return;\n            }\n\n            // idObject == 0 means it is the window itself, not a child object\n            if (eventType == NativeHelper.EVENT_OBJECT_SHOW && idObject == 0)\n            {","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/presentation/Stride.Core.Presentation.Wpf/Windows/WindowManager.cs#L245-L281","documentation":"WindowManager is single-threaded by design: all its public APIs must run on the WPF dispatcher thread that owns the underlying Dispatcher. CheckDispatcher compares the dispatcher's thread with the current thread and throws InvalidOperationException on mismatch.","triggerScenarios":"Calling ShowMainWindow, ShowBlockingWindow, or any other WindowManager method from a background thread, Task.Run body, or non-UI event (network callback, timer thread).","commonSituations":"Showing a dialog from async work or a worker thread; showing a window from a background service callback; tests invoking WindowManager off the STA UI thread.","solutions":["Marshal the call to the dispatcher: dispatcher.Invoke(() => windowManager.ShowMainWindow(w)) or Dispatcher.InvokeAsync","Store the UI Dispatcher at startup and always route window operations through it","Use a helper like Application.Current.Dispatcher.InvokeAsync for window operations","Fix the calling code so window management stays on the UI thread (await on UI SynchronizationContext)"],"exampleFix":"// before (background thread)\nwindowManager.ShowBlockingWindow(dialog);\n// after\nApplication.Current.Dispatcher.Invoke(() => windowManager.ShowBlockingWindow(dialog));","handlingStrategy":"try-catch","validationCode":"bool onDispatcher = Application.Current.Dispatcher.CheckAccess();","typeGuard":null,"tryCatchPattern":"if (Application.Current.Dispatcher.CheckAccess()) DoWindowWork(); else Application.Current.Dispatcher.Invoke(DoWindowWork);","preventionTips":["Always marshal window operations via Dispatcher.Invoke/InvokeAsync","Avoid touching WindowManager from background threads or async continuations on the pool","Enable thread checks in debug to catch violations early"],"tags":["wpf","threading","dispatcher"],"backgroundTag":"invalid-state-transition","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}