Fix WindowPersistence when closing from FullScreen#256
Fix WindowPersistence when closing from FullScreen#256botMorten wants to merge 2 commits intodotMorten:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes a window persistence race where closing while in a non-overlapped presenter (notably FullScreen) could persist fullscreen-sized bounds instead of the prior overlapped placement, leading to incorrect restore geometry on next launch.
Changes:
- Added deferred overlapped
WINDOWPLACEMENTcapture and caching of the last known overlapped placement. - Triggered placement capture on state/position/presenter transitions and after persistence restore.
- Updated
SavePersistence()to persist cached overlapped placement when current presenter isn’t overlapped.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/WinUIEx/WindowManager.cs
Outdated
| _window.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () => | ||
| { | ||
| if (version != _overlappedPlacementUpdateVersion) | ||
| return; | ||
| TryCaptureOverlappedPlacement(); | ||
| }); | ||
| } | ||
|
|
||
| private bool TryCaptureOverlappedPlacement() | ||
| { |
There was a problem hiding this comment.
The capture callback can run after the window manager is disposed/closing (it’s queued asynchronously and not cancelled). In that case GetWindowHandle()/GetWindowPlacement may fail or throw. Add a guard (e.g., check _isDisposed / window handle validity) inside the queued callback and/or skip scheduling when disposed. Also consider handling TryEnqueue returning false (e.g., fall back to synchronous capture).
| _window.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () => | |
| { | |
| if (version != _overlappedPlacementUpdateVersion) | |
| return; | |
| TryCaptureOverlappedPlacement(); | |
| }); | |
| } | |
| private bool TryCaptureOverlappedPlacement() | |
| { | |
| // If we've already been disposed, don't schedule any more work. | |
| if (_isDisposed) | |
| return; | |
| bool enqueued = _window.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () => | |
| { | |
| // The callback may run after dispose; bail out early in that case. | |
| if (_isDisposed) | |
| return; | |
| if (version != _overlappedPlacementUpdateVersion) | |
| return; | |
| TryCaptureOverlappedPlacement(); | |
| }); | |
| // If we failed to enqueue (e.g., dispatcher is shutting down), try capturing synchronously. | |
| if (!enqueued && !_isDisposed && version == _overlappedPlacementUpdateVersion) | |
| { | |
| TryCaptureOverlappedPlacement(); | |
| } | |
| } | |
| private bool TryCaptureOverlappedPlacement() | |
| { | |
| // Avoid accessing window/handle after disposal. | |
| if (_isDisposed || _window is null) | |
| return false; |
|
Thanks — great catches. I pushed follow-up commit addressing the review feedback:
On test coverage: agreed on adding a fullscreen-close persistence regression test. I can add that in a follow-up if maintainers want it in this PR (I wasn’t able to run local WinUI test builds in my current environment). |
|
Thanks — great catches. I pushed a follow-up commit addressing the review feedback:
On test coverage: agreed on adding a fullscreen-close persistence regression test. I can add that in a follow-up if maintainers want it in this PR (I wasn’t able to run local WinUI test builds in my current environment). |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Fixes a persistence race where closing while in FullScreen could save fullscreen-sized bounds instead of the prior overlapped placement.
Root cause
Window size/bounds changes can fire before presenter transition settles, so persistence captured at close could be fullscreen geometry.
Changes
Notes
This preserves existing default behavior (do not persist fullscreen presenter kind), while avoiding corrupted restore geometry after closing from fullscreen.