Compare commits

...

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
02e149d247 Fix Settings crash: eliminate off-UI-thread DependencyProperty updates in conflict detection
The crash (STATUS_STOWED_EXCEPTION / 0xc000027b in CoreMessagingXP.dll) was caused by
two threading bugs in the hotkey conflict detection feature:

1. PageViewModelBase.OnConflictsUpdated: The original code had a Task.Run with a
   catch block that called UpdateConflictProperties() directly on a Task thread when
   settingsWindow was null. UpdateConflictProperties() sets HotkeySettings.HasConflict
   and HotkeySettings.ConflictDescription, which raise PropertyChanged. With active
   x:Bind bindings, WinUI3 propagated those notifications as a stowed exception through
   CoreMessagingXP.dll, crashing the process.

   Fixed by removing the unsafe catch-block fallback and the unnecessary Task.Run
   wrapper. TryEnqueue is non-blocking and thread-safe, so it can be called directly
   on the IPC thread. If the settings window is null, the update is skipped (correct
   behavior when there is no UI to update).

2. ShortcutConflictViewModel: Used WPF's System.Windows.Threading.Dispatcher
   (Dispatcher.CurrentDispatcher + BeginInvoke) in a WinUI3 app, mixing threading
   models. Replaced with Microsoft.UI.Dispatching.DispatcherQueue captured via
   DispatcherQueue.GetForCurrentThread() in the constructor, matching the pattern
   used by DashboardViewModel.

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/2dc7795f-f2bf-4e8d-992b-2a5deeefae23

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 11:13:41 +00:00
copilot-swe-agent[bot]
34d893ef0e Initial plan 2026-04-29 08:49:50 +00:00
2 changed files with 6 additions and 17 deletions

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
@@ -71,18 +70,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
_ = Task.Run(() =>
{
try
{
var settingsWindow = App.GetSettingsWindow();
settingsWindow.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, UpdateConflictProperties);
}
catch
{
UpdateConflictProperties();
}
});
var dispatcherQueue = App.GetSettingsWindow()?.DispatcherQueue;
dispatcherQueue?.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, UpdateConflictProperties);
}
public virtual Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()

View File

@@ -12,8 +12,8 @@ using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Windows.Threading;
using ManagedCommon;
using Microsoft.UI.Dispatching;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
@@ -29,7 +29,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
private readonly SettingsFactory _settingsFactory;
private readonly Func<string, int> _ipcMSGCallBackFunc;
private readonly Dispatcher _dispatcher;
private readonly DispatcherQueue _dispatcherQueue;
private bool _disposed;
private AllHotkeyConflictsData _conflictsData = new();
@@ -41,7 +41,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
ISettingsRepository<GeneralSettings> settingsRepository,
Func<string, int> ipcMSGCallBackFunc)
{
_dispatcher = Dispatcher.CurrentDispatcher;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
_ipcMSGCallBackFunc = ipcMSGCallBackFunc ?? throw new ArgumentNullException(nameof(ipcMSGCallBackFunc));
resourceLoader = ResourceLoaderInstance.ResourceLoader;
@@ -123,7 +123,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
{
_dispatcher.BeginInvoke(() =>
_dispatcherQueue?.TryEnqueue(() =>
{
ConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
});