Skip to content

[Shortcut Guide] Prevent overlay crash on section navigation (#48448)#48481

Open
niels9001 wants to merge 2 commits into
microsoft:mainfrom
niels9001:niels9001/fix-shortcut-guide-nav-crash-48448
Open

[Shortcut Guide] Prevent overlay crash on section navigation (#48448)#48481
niels9001 wants to merge 2 commits into
microsoft:mainfrom
niels9001:niels9001/fix-shortcut-guide-nav-crash-48448

Conversation

@niels9001

@niels9001 niels9001 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary of the Pull Request

Shortcut Guide overlay crashes and closes when navigating between sidebar sections (e.g. clicking PowerToys, then clicking the Windows icon to come back). Repro and crash logs in #48448. Crash logs in #48441 show the same propagation path plus a follow-up access violation in coreclr, indicating exceptions that escape local catches.

This PR fixes the immediate navigation race and adds broader crash hardening so future exceptions on the UI/background threads are logged instead of tearing down the overlay.

Navigation-race fix (commit 1)

Root cause: WindowSelector_SelectionChanged calls App.TaskBarWindow.Activate() and then SetWindowPosition() synchronously. Activate() runs a reentrant Window_ActivatedBringToFrontTaskbarWindow.Activated chain that can leave App.TaskBarWindow.AppWindow momentarily null, so SetWindowPosition throws NullReferenceException.

Because the initial SelectedItem = MenuItems[0] is set from SetNavItems, the NRE bubbles up into InitializeNavItemsAsync's catch block — which sets _closeType = "InitializationFailed" and closes the window. That matches both user-visible symptoms: the overlay "flashes and disappears" (#48441) on open and "closes when clicking the Windows icon" (#48448).

Edits in src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs:

  • SetWindowPosition: null-guard App.TaskBarWindow?.AppWindow and skip the taskbar-overlap height adjustment when it is not currently observable. Wrap the body in try/catch with Logger.LogError so any future positioning hiccup keeps the previous layout instead of taking down the overlay.
  • WindowSelector_SelectionChanged: null-guard App.TaskBarWindow?.Hide() / Activate() and wrap the body in try/catch. This breaks the propagation path that lets a navigation exception reach InitializeNavItemsAsync's "fatal init failure" branch.

Crash hardening (commit 2)

Additional defensive changes to cover other unguarded paths surfaced while reviewing #48441:

  • App.xaml.cs: register App.UnhandledException, AppDomain.CurrentDomain.UnhandledException, and TaskScheduler.UnobservedTaskException so a stray exception (e.g. an IO failure during a fire-and-forget UI handler, or a background Task fault) gets logged instead of tearing the process down with an access violation in coreclr. Mirrors what Peek, AdvancedPaste, and CmdPal already do.
  • App.OnLaunched: wrap the launch sequence in try/catch and exit cleanly with an error log on failure (LoadData / MainWindow / TaskbarWindow ctors and Activate are all reachable failure points).
  • App.LoadData: broaden the Pinned.json deserialize catch to also handle IOException / UnauthorizedAccessException, and guard the round-trip SaveSettings call as best-effort with a warning log.
  • PinnedShortcutsHelper.Save: catch IOException / UnauthorizedAccessException / JsonException and log; Pin / Unpin runs from a synchronous UI handler so an unguarded File.WriteAllText would tear down the overlay on any disk hiccup.
  • TaskbarWindow.UpdateTasklistButtons: move the AppWindow.Move calls inside the existing try block, null-guard App.MainWindow?.AppWindow up front, and wrap the whole body so the method (which runs from the ctor and from Activated) cannot tear the overlay down when MainWindow is in a transient state.

PR Checklist

Detailed Description of the Pull Request / Additional comments

The fix is intentionally defensive (rather than restructuring the reentrant activation flow) because the legacy taskbar UIA enumeration (TasklistPositions.GetButtons) on Windows 10 is what most reliably widens the race window and is out of scope to redesign for a hotfix.

Validation Steps Performed

  • Build: tools\build\build.cmd from src\modules\ShortcutGuide\ShortcutGuide.Ui — exit 0, errors log empty for both commits.
  • Synthetic repro: temporarily injected throw new NullReferenceException() at the top of SetWindowPosition to exercise the exact propagation path the reporter's log shows (SetWindowPositionSelectionChangedset_SelectedItemInitializeNavItemsAsync.catchClose("InitializationFailed")).
    • Before fix: overlay flashes and closes, log shows Failed to initialize navigation items. with the NRE.
    • After fix: overlay stays open, page navigates, log shows Failed to set Shortcut Guide window position; keeping previous layout. from the new catch.
  • Did not reproduce the live race on a Win11 dev box; the reporter's repro is Windows 10 19045 / Microsoft Store install where the legacy taskbar UIA timing makes the reentrant chain more likely to expose the null.

…ft#48448)

WindowSelector_SelectionChanged invokes App.TaskBarWindow.Activate() and then SetWindowPosition() synchronously. Activate() runs a reentrant Window_Activated/BringToFront/TaskbarWindow.Activated chain that can leave App.TaskBarWindow.AppWindow null momentarily, throwing NRE inside SetWindowPosition.

Because SelectionChanged is fired from SetNavItems' initial SelectedItem assignment, the NRE bubbles up to InitializeNavItemsAsync's catch and closes the window with _closeType=InitializationFailed, matching the user-visible 'flash and disappear' / 'overlay closes when clicking Windows icon' reports.

- Null-guard App.TaskBarWindow?.AppWindow in SetWindowPosition; skip the overlap adjustment when unavailable.
- Wrap SetWindowPosition body in try/catch with Logger.LogError so any future positioning failure keeps the previous layout instead of taking down the overlay.
- Null-guard App.TaskBarWindow?.Hide() / Activate() and wrap WindowSelector_SelectionChanged body in try/catch so an exception during navigation no longer reaches InitializeNavItemsAsync's failure path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the Shortcut Guide overlay against a reentrancy-related crash when navigating between sidebar sections by preventing exceptions in the selection-change/positioning path from propagating into initialization failure logic.

Changes:

  • Wraps SetWindowPosition() in a try/catch and skips taskbar overlap calculations when App.TaskBarWindow.AppWindow is temporarily unavailable.
  • Wraps WindowSelector_SelectionChanged in a try/catch and adds null-guards around taskbar window interactions to avoid closing the overlay on navigation-time exceptions.
  • Adds new error logging for both the positioning and section-selection failure paths.

Comment on lines +284 to +287
catch (Exception ex)
{
Logger.LogError("Failed to set Shortcut Guide window position; keeping previous layout.", ex);
}
Comment on lines +312 to +316
if (file.Shortcuts is not null && file.Shortcuts.Any(c => c.SectionName?.StartsWith("<TASKBAR1-9>", StringComparison.Ordinal) == true))
{
this._taskBarWindowActivated = true;
App.TaskBarWindow?.Activate();
}
…aunch + IO guards)

Complements the navigation-race fix from the previous commit with broader
crash hardening based on review of issue microsoft#48441 (overlay flashes and
disappears, coreclr.dll access violation).

- App.xaml.cs: register App.UnhandledException, AppDomain.CurrentDomain.
  UnhandledException, and TaskScheduler.UnobservedTaskException so a stray
  exception from a fire-and-forget UI handler or a background Task fault
  gets logged instead of taking the overlay down with an unhandled access
  violation in coreclr. Mirrors what Peek, AdvancedPaste, and CmdPal
  already do.

- App.OnLaunched: wrap launch sequence in try/catch and exit cleanly with
  an error log on failure (LoadData / MainWindow / TaskbarWindow ctors and
  Activate are all reachable failure points).

- App.LoadData: broaden the Pinned.json deserialize catch to also handle
  IOException / UnauthorizedAccessException, and guard the round-trip
  SaveSettings call as best-effort with a warning.

- PinnedShortcutsHelper.Save: catch IO / Json failures and log; Pin/Unpin
  runs from a synchronous UI handler so an unguarded File.WriteAllText
  would tear down the overlay on any disk hiccup.

- TaskbarWindow.UpdateTasklistButtons: move the AppWindow.Move calls
  inside the try block, null-guard App.MainWindow?.AppWindow up front,
  and wrap the whole body so the method (which runs from the ctor and
  from Activated) cannot tear the overlay down when MainWindow is in a
  transient state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot requested a review from chatasweetie June 13, 2026 14:13
@github-actions

Copy link
Copy Markdown

Thank you for contributing to PowerToys. We've detected that this PR might include a new or modified telemetry event. Please ensure the following before merging:

@niels9001 niels9001 requested a review from noraa-junker June 13, 2026 14:16
@niels9001 niels9001 added Product-Shortcut Guide Refers to the Shortcut Guide PowerToy Hot Fix Items we will product an out-of-band release for labels Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hot Fix Items we will product an out-of-band release for Product-Shortcut Guide Refers to the Shortcut Guide PowerToy

Projects

None yet

2 participants