You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The shared Outlook folder-tree service can invoke WpfDispatcherYield from a dispatcher-free worker after EmailDataMiner starts a cold cache build through Task.Run. Dispatcher.Yield then raises InvalidOperationException, and a fallback that only uses Task.Yield would not restore the required Outlook STA traversal contract.
Environment
OS/version: Windows with Outlook VSTO runtime
Application: TaskMaster Outlook add-in
Trigger: Cold folder-tree cache build or refresh that reaches a deadline-controlled yield point
Data source or fixture: Outlook folder hierarchy through the live COM adapter
Steps to Reproduce
Start the email-mining path while the shared folder-tree cache requires a build or refresh.
EmailDataMiner.MineEmails invokes ExtractOlFolderChunks through Task.Run.
The folder-tree reader reaches WpfDispatcherYield.YieldAsync after the deadline clock requests a yield.
Expected Behavior
Live Outlook COM traversal and cooperative yielding remain on the captured Outlook STA dispatcher, and the caller receives a folder snapshot without a thread-affinity exception.
Actual Behavior
WpfDispatcherYield invokes Dispatcher.Yield(DispatcherPriority.Background) on a worker thread that has no current dispatcher. WPF raises InvalidOperationException indicating that the calling thread does not have a current Dispatcher.
Delivered by #422 (merged 2026-08-07, merge commit 0d85748) and the follow-up #423 (merged 2026-08-07, merge commit fb32b92). Neither PR body used a closing keyword, so this issue was not auto-closed.
Verified against origin/main:
UtilitiesCS/OutlookObjects/Folder/WpfDispatcherYield.cs no longer calls Dispatcher.Yield(). It resolves a dispatcher explicitly as Dispatcher.FromThread(Thread.CurrentThread) ?? UtilitiesCS.UiThread.Dispatcher, posts a background-priority InvokeAsync, and throws InvalidOperationException with an actionable message when no dispatcher is available.
Folder-tree service composition, cold builds, refreshes, and notification cleanup are marshaled through the captured Outlook STA dispatcher, so the thread-first resolution preserves the STA traversal contract rather than falling back to the process-global dispatcher.
Regression coverage exists at WpfDispatcherYieldTests.YieldAsync_WithoutDispatcher_RemainsStrict and OutlookFolderTreeServiceConcurrencyTests.GetSnapshotAsync_WorkerOriginatedColdBuild_UsesCapturedStaDispatcher.
Expected behavior in this issue (traversal and yielding remain on the captured Outlook STA dispatcher, no thread-affinity exception) is satisfied.
Activity
fix(folder-tree): prevent post-disposal UI mutations
fix(folder-tree): keep traversal on the Outlook STA dispatcher
fix(folder-tree): prevent post-disposal UI mutations
drmoisan commentedon Aug 8, 2026
Delivered by #422 (merged 2026-08-07, merge commit 0d85748) and the follow-up #423 (merged 2026-08-07, merge commit fb32b92). Neither PR body used a closing keyword, so this issue was not auto-closed.
Verified against
origin/main:UtilitiesCS/OutlookObjects/Folder/WpfDispatcherYield.csno longer callsDispatcher.Yield(). It resolves a dispatcher explicitly asDispatcher.FromThread(Thread.CurrentThread) ?? UtilitiesCS.UiThread.Dispatcher, posts a background-priorityInvokeAsync, and throwsInvalidOperationExceptionwith an actionable message when no dispatcher is available.WpfDispatcherYieldTests.YieldAsync_WithoutDispatcher_RemainsStrictandOutlookFolderTreeServiceConcurrencyTests.GetSnapshotAsync_WorkerOriginatedColdBuild_UsesCapturedStaDispatcher.Expected behavior in this issue (traversal and yielding remain on the captured Outlook STA dispatcher, no thread-affinity exception) is satisfied.
Closing as delivered.