Files
PowerToys/installer
Boliang Zhang 389b1a3827 fix(shortcut-guide): prevent orphaned processes during shutdown (#50091)
## Summary of the Pull Request

Prevents Shortcut Guide processes from surviving Runner shutdown and
blocking PowerToys upgrades.

- Shuts down WinUI through its dispatcher instead of forcing CLR
termination from a worker thread.
- Opens and retains the Runner process handle before WinUI
initialization so early Runner exits cannot be missed.
- Gives the native module deterministic ownership of the Shortcut Guide
process handle, with graceful shutdown and a bounded forced-termination
fallback.
- Keeps telemetry subprocess handles separate from the persistent UI
process.
- Adds `PowerToys.ShortcutGuide.exe` to the installer termination
fallback so affected existing installations can recover during upgrade.

## PR Checklist

- [x] **Communication:** Discussed and requested by a core contributor
after investigating the release regression.
- [x] **Tests:** Existing tests pass; process lifecycle and installer
file replacement were also validated.

## Detailed Description of the Pull Request / Additional comments

The Shortcut Guide lifecycle introduced by #48683 could call
`Environment.Exit` from a Runner-watcher worker thread while WinUI was
still tearing down. The native module also overwrote its persistent
child-process handle when launching telemetry and did not close
completed handles. Repeated Runner lifetimes could therefore leave
`PowerToys.ShortcutGuide.exe` processes retaining shared WinUI files.

The installer did not recover from that state: Restart Manager is
disabled, the bundle and WiX close-application steps target only
`PowerToys.exe`, and `TerminateProcessesCA` did not include
`PowerToys.ShortcutGuide.exe`. Locked files could consequently remain at
the previous version while installation continued, producing a mixed
payload.

`src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs` now
synchronously captures the Runner process handle and publishes its exit
through a wait handle.
`src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml.cs`
registers that wait with the UI dispatcher, centralizes idempotent
shutdown, and disposes activation listeners and hooks deterministically.

`src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp` now
uses RAII for the tracked UI process, avoids replacing it with telemetry
handles, signals the existing native exit event, waits for graceful
shutdown, and terminates only as a bounded fallback. The event is
projected to managed code through `src/common/interop/Constants.idl`.

`installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp` now
includes `PowerToys.ShortcutGuide.exe` in the MSI process-termination
fallback, allowing upgrades from already-affected builds.

## Validation Steps Performed

- Built `PowerToys.Interop.vcxproj`,
`ShortcutGuideModuleInterface.vcxproj`, `ShortcutGuide.Ui.csproj`, and
`PowerToysSetupCustomActionsVNext.vcxproj` for x64 Release.
- Built and ran `ShortcutGuide.UnitTests`: 48/48 passed.
- Repeated Runner/Shortcut Guide startup and parent-exit teardown 10
times; every child exited with code 0.
- Verified the race where the Runner exits before Shortcut Guide
initializes.
- Verified `PowerToys.ShortcutGuide.exe`, `PowerToys.Interop.dll`, and
`Microsoft.UI.Xaml.dll` were immediately replaceable after shutdown.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7f6c4822-53e0-42ae-a51a-a302a7c6d3ab
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7b3fb20d-6e9d-4fef-a5cd-f8921d28c220
2026-08-25 13:18:26 +08:00
..