fix(shortcutguide): restore excluded-app filtering regression (#50046)

## Summary of the Pull Request

**Regression:** PR #48683 changed Shortcut Guide from a process launched
per invocation to a
persistent background process. Before that change, every activation
started a process and evaluated
the current foreground app against the latest excluded-app settings. The
excluded-app check
remained in `Program.Main`, so after #48683 it ran only when the
persistent process started.
Excluded apps therefore stopped blocking later regular-hotkey and
Windows-key-hold activations, and
newly saved exclusions had no effect until the process was recycled.

This PR restores the pre-#48683 excluded-app behavior by:

- starting the persistent Shortcut Guide listener regardless of the
startup foreground app;
- evaluating the current foreground app against the latest excluded-app
settings before each
  hidden-overlay activation;
- applying the same gate to the regular hotkey and Windows-key-hold
paths; and
- preserving close and hold-surface promotion behavior when the overlay
is already visible.

## PR Checklist

- [x] Closes: #50030
- [ ] **Communication:** This regression was diagnosed from the issue
and recent Shortcut Guide
lifecycle changes; it has not yet been discussed with core contributors
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** N/A - no end-user-facing strings were added or
changed
- [x] **Dev docs:** N/A - no public behavior or developer contract
changed
- [x] **New binaries:** N/A - no binaries or projects were added
- [x] **Documentation updated:** N/A - this is a regression fix
restoring existing behavior

## Detailed Description of the Pull Request / Additional comments

`src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs` no longer exits
at process startup when the
then-foreground application is excluded. The background listener must
remain available for later
activations.


`src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml.cs`
calls the existing native
excluded-app helper for each activation while the overlay is hidden.
That helper already reloads
`settings.json` and rebuilds its excluded-app list on every call, so no
watcher or cache is needed.
The result is passed into

`src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ShortcutGuideActivationPolicy.cs`,
keeping the
decision shared by both activation sources and directly testable.

The check is intentionally skipped while the overlay is visible. This
preserves the regular
hotkey's ability to close its guide or take ownership of a guide opened
by Windows-key hold.
Suppression logging is generic and does not include application names,
paths, window titles, or
settings content.

This is a regression fix rather than a new excluded-app feature or
settings-schema change.

## Validation Steps Performed

- Built
`src/modules/ShortcutGuide/ShortcutGuide.UnitTests/ShortcutGuide.UnitTests.csproj`
for x64
  Release from the final rebased commit.
- Ran the Shortcut Guide test executable: **48 passed, 0 failed, 0
skipped**.
- Added cases covering:
  - regular-hotkey suppression over an excluded app;
- Windows-key-hold suppression for taskbar indicators and the full
guide;
  - closing a visible regular guide; and
  - promoting visible hold indicators with the regular hotkey.
- Built a complete local x64 Release payload and confirmed its Runner
launched the local Shortcut
  Guide process and handled open/close activation.

Copilot-Session: 8271bded-18e8-474e-8e3b-addd71f67f50
This commit is contained in:
Boliang Zhang
2026-08-21 14:13:47 +08:00
committed by GitHub
parent d3eccba55d
commit 5ec457ea44
4 changed files with 64 additions and 5 deletions

View File

@@ -33,10 +33,16 @@ public static class ShortcutGuideActivationPolicy
public static ShortcutGuideActivationAction GetActivationAction( public static ShortcutGuideActivationAction GetActivationAction(
ShortcutGuideActivationSource activationSource, ShortcutGuideActivationSource activationSource,
bool isOverlayVisible, bool isOverlayVisible,
bool isCurrentWindowExcluded,
ShortcutGuideActivationSource activeSource, ShortcutGuideActivationSource activeSource,
ShortcutGuideOverlaySurface activeSurface, ShortcutGuideOverlaySurface activeSurface,
ShortcutGuideWindowsKeyAction windowsKeyAction) ShortcutGuideWindowsKeyAction windowsKeyAction)
{ {
if (!isOverlayVisible && isCurrentWindowExcluded)
{
return ShortcutGuideActivationAction.None;
}
if (!isOverlayVisible) if (!isOverlayVisible)
{ {
activeSource = ShortcutGuideActivationSource.None; activeSource = ShortcutGuideActivationSource.None;

View File

@@ -55,11 +55,6 @@ namespace ShortcutGuide
Directory.CreateDirectory(ManifestInterpreter.PathOfManifestFiles); Directory.CreateDirectory(ManifestInterpreter.PathOfManifestFiles);
if (NativeMethods.IsCurrentWindowExcludedFromShortcutGuide())
{
return;
}
// Copy every shipped manifest from the install directory to the per-user manifest folder. // Copy every shipped manifest from the install directory to the per-user manifest folder.
// Enumerating the source folder avoids drift between the deployed assets and a hard-coded list. // Enumerating the source folder avoids drift between the deployed assets and a hard-coded list.
// Todo: Only copy files after an update. // Todo: Only copy files after an update.

View File

@@ -251,16 +251,24 @@ namespace ShortcutGuide
try try
{ {
bool isOverlayVisible = OverlayWindow.AppWindow.IsVisible; bool isOverlayVisible = OverlayWindow.AppWindow.IsVisible;
bool isCurrentWindowExcluded =
!isOverlayVisible && NativeMethods.IsCurrentWindowExcludedFromShortcutGuide();
var activeSource = (ShortcutGuideActivationSource)Volatile.Read(ref _activeSource); var activeSource = (ShortcutGuideActivationSource)Volatile.Read(ref _activeSource);
var activeSurface = (ShortcutGuideOverlaySurface)Volatile.Read(ref _activeSurface); var activeSurface = (ShortcutGuideOverlaySurface)Volatile.Read(ref _activeSurface);
var windowsKeyAction = (ShortcutGuideWindowsKeyAction)ShortcutGuideProperties.WindowsKeyAction.Value; var windowsKeyAction = (ShortcutGuideWindowsKeyAction)ShortcutGuideProperties.WindowsKeyAction.Value;
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
activationSource, activationSource,
isOverlayVisible, isOverlayVisible,
isCurrentWindowExcluded,
activeSource, activeSource,
activeSurface, activeSurface,
windowsKeyAction); windowsKeyAction);
if (isCurrentWindowExcluded)
{
Logger.LogInfo("Shortcut Guide activation suppressed because the foreground application is excluded.");
}
Logger.LogInfo($"Shortcut Guide activation action: {action}."); Logger.LogInfo($"Shortcut Guide activation action: {action}.");
switch (action) switch (action)
{ {

View File

@@ -22,6 +22,7 @@ public sealed class ShortcutGuideActivationPolicyTests
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.WindowsKeyHold, ShortcutGuideActivationSource.WindowsKeyHold,
isOverlayVisible: false, isOverlayVisible: false,
isCurrentWindowExcluded: false,
ShortcutGuideActivationSource.None, ShortcutGuideActivationSource.None,
ShortcutGuideOverlaySurface.Hidden, ShortcutGuideOverlaySurface.Hidden,
windowsKeyAction); windowsKeyAction);
@@ -38,6 +39,7 @@ public sealed class ShortcutGuideActivationPolicyTests
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideActivationSource.RegularHotkey,
isOverlayVisible: false, isOverlayVisible: false,
isCurrentWindowExcluded: false,
ShortcutGuideActivationSource.None, ShortcutGuideActivationSource.None,
ShortcutGuideOverlaySurface.Hidden, ShortcutGuideOverlaySurface.Hidden,
windowsKeyAction); windowsKeyAction);
@@ -51,6 +53,7 @@ public sealed class ShortcutGuideActivationPolicyTests
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideActivationSource.RegularHotkey,
isOverlayVisible: true, isOverlayVisible: true,
isCurrentWindowExcluded: false,
ShortcutGuideActivationSource.WindowsKeyHold, ShortcutGuideActivationSource.WindowsKeyHold,
ShortcutGuideOverlaySurface.TaskbarIndicators, ShortcutGuideOverlaySurface.TaskbarIndicators,
ShortcutGuideWindowsKeyAction.TaskbarIndicators); ShortcutGuideWindowsKeyAction.TaskbarIndicators);
@@ -64,6 +67,7 @@ public sealed class ShortcutGuideActivationPolicyTests
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideActivationSource.RegularHotkey,
isOverlayVisible: true, isOverlayVisible: true,
isCurrentWindowExcluded: false,
ShortcutGuideActivationSource.WindowsKeyHold, ShortcutGuideActivationSource.WindowsKeyHold,
ShortcutGuideOverlaySurface.FullGuide, ShortcutGuideOverlaySurface.FullGuide,
ShortcutGuideWindowsKeyAction.OpenShortcutGuide); ShortcutGuideWindowsKeyAction.OpenShortcutGuide);
@@ -77,6 +81,7 @@ public sealed class ShortcutGuideActivationPolicyTests
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideActivationSource.RegularHotkey,
isOverlayVisible: true, isOverlayVisible: true,
isCurrentWindowExcluded: false,
ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideActivationSource.RegularHotkey,
ShortcutGuideOverlaySurface.FullGuide, ShortcutGuideOverlaySurface.FullGuide,
ShortcutGuideWindowsKeyAction.TaskbarIndicators); ShortcutGuideWindowsKeyAction.TaskbarIndicators);
@@ -90,6 +95,7 @@ public sealed class ShortcutGuideActivationPolicyTests
var action = ShortcutGuideActivationPolicy.GetActivationAction( var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.WindowsKeyHold, ShortcutGuideActivationSource.WindowsKeyHold,
isOverlayVisible: true, isOverlayVisible: true,
isCurrentWindowExcluded: false,
ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideActivationSource.RegularHotkey,
ShortcutGuideOverlaySurface.FullGuide, ShortcutGuideOverlaySurface.FullGuide,
ShortcutGuideWindowsKeyAction.OpenShortcutGuide); ShortcutGuideWindowsKeyAction.OpenShortcutGuide);
@@ -97,6 +103,50 @@ public sealed class ShortcutGuideActivationPolicyTests
Assert.AreEqual(ShortcutGuideActivationAction.None, action); Assert.AreEqual(ShortcutGuideActivationAction.None, action);
} }
[TestMethod]
[DataRow(ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideWindowsKeyAction.TaskbarIndicators)]
[DataRow(ShortcutGuideActivationSource.WindowsKeyHold, ShortcutGuideWindowsKeyAction.TaskbarIndicators)]
[DataRow(ShortcutGuideActivationSource.WindowsKeyHold, ShortcutGuideWindowsKeyAction.OpenShortcutGuide)]
public void GetActivationAction_ExcludedApp_SuppressesHiddenOverlay(
ShortcutGuideActivationSource activationSource,
ShortcutGuideWindowsKeyAction windowsKeyAction)
{
var action = ShortcutGuideActivationPolicy.GetActivationAction(
activationSource,
isOverlayVisible: false,
isCurrentWindowExcluded: true,
ShortcutGuideActivationSource.None,
ShortcutGuideOverlaySurface.Hidden,
windowsKeyAction);
Assert.AreEqual(ShortcutGuideActivationAction.None, action);
}
[TestMethod]
[DataRow(
ShortcutGuideActivationSource.RegularHotkey,
ShortcutGuideOverlaySurface.FullGuide,
ShortcutGuideActivationAction.Close)]
[DataRow(
ShortcutGuideActivationSource.WindowsKeyHold,
ShortcutGuideOverlaySurface.TaskbarIndicators,
ShortcutGuideActivationAction.ShowFullGuide)]
public void GetActivationAction_ExcludedApp_DoesNotSuppressVisibleOverlay(
ShortcutGuideActivationSource activeSource,
ShortcutGuideOverlaySurface activeSurface,
ShortcutGuideActivationAction expected)
{
var action = ShortcutGuideActivationPolicy.GetActivationAction(
ShortcutGuideActivationSource.RegularHotkey,
isOverlayVisible: true,
isCurrentWindowExcluded: true,
activeSource,
activeSurface,
ShortcutGuideWindowsKeyAction.TaskbarIndicators);
Assert.AreEqual(expected, action);
}
[TestMethod] [TestMethod]
[DataRow(ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideOverlaySurface.FullGuide, true, false)] [DataRow(ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideOverlaySurface.FullGuide, true, false)]
[DataRow(ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideOverlaySurface.FullGuide, false, false)] [DataRow(ShortcutGuideActivationSource.RegularHotkey, ShortcutGuideOverlaySurface.FullGuide, false, false)]