diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesDataService.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesDataService.cs
index 639128d7f7..d81a6f9e11 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesDataService.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesDataService.cs
@@ -41,16 +41,13 @@ internal static class FancyZonesDataService
try
{
- if (!File.Exists(FZPaths.EditorParameters))
- {
- error = Resources.FancyZones_MonitorDataNotFound;
- Logger.LogWarning($"TryGetMonitors: File not found. Path={FZPaths.EditorParameters}");
- return false;
- }
-
- Logger.LogInfo($"TryGetMonitors: File exists, reading...");
- var editorParams = FancyZonesDataIO.ReadEditorParameters();
- Logger.LogInfo($"TryGetMonitors: ReadEditorParameters returned. Monitors={editorParams.Monitors?.Count ?? -1}");
+ // Request FancyZones to save current monitor configuration.
+ // The editor-parameters.json file is only written when:
+ // 1. Opening the FancyZones Editor
+ // 2. Receiving the WM_PRIV_SAVE_EDITOR_PARAMETERS message
+ // Without this, monitor changes (plug/unplug) won't be reflected in the file.
+ var editorParams = ReadEditorParametersWithRefresh();
+ Logger.LogInfo($"TryGetMonitors: ReadEditorParametersWithRefreshWithRefresh returned. Monitors={editorParams.Monitors?.Count ?? -1}");
var editorMonitors = editorParams.Monitors;
if (editorMonitors is null || editorMonitors.Count == 0)
@@ -74,6 +71,23 @@ internal static class FancyZonesDataService
}
}
+ ///
+ /// Requests FancyZones to save the current monitor configuration and reads the file.
+ /// This is a best-effort approach for performance: we send the save request and immediately
+ /// read the file without waiting. If the file hasn't been updated yet, the next call will
+ /// see the updated data since FancyZones processes the message asynchronously.
+ ///
+ private static EditorParameters.ParamsWrapper ReadEditorParametersWithRefresh()
+ {
+ // Request FancyZones to save the current monitor configuration.
+ // This is fire-and-forget for performance - we don't wait for the save to complete.
+ // If this is the first call after a monitor change, we may read stale data, but the
+ // next call will see the updated file since FancyZones will have processed the message.
+ FancyZonesNotifier.NotifySaveEditorParameters();
+
+ return FancyZonesDataIO.ReadEditorParameters();
+ }
+
public static IReadOnlyList GetLayouts()
{
Logger.LogInfo($"GetLayouts: Starting. LayoutTemplatesPath={FZPaths.LayoutTemplates} CustomLayoutsPath={FZPaths.CustomLayouts}");
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesNotifier.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesNotifier.cs
index ada8ee7b17..256331a951 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesNotifier.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesNotifier.cs
@@ -10,13 +10,25 @@ namespace PowerToysExtension.Helpers;
internal static class FancyZonesNotifier
{
private const string AppliedLayoutsFileUpdateMessage = "{2ef2c8a7-e0d5-4f31-9ede-52aade2d284d}";
+ private const string SaveEditorParametersMessage = "{d8f9c0e3-5d77-4e83-8a4f-7c704c2bfb4a}";
+
private static readonly uint WmPrivAppliedLayoutsFileUpdate = RegisterWindowMessageW(AppliedLayoutsFileUpdateMessage);
+ private static readonly uint WmPrivSaveEditorParameters = RegisterWindowMessageW(SaveEditorParametersMessage);
public static void NotifyAppliedLayoutsChanged()
{
_ = PostMessageW(new IntPtr(0xFFFF), WmPrivAppliedLayoutsFileUpdate, UIntPtr.Zero, IntPtr.Zero);
}
+ ///
+ /// Notifies FancyZones to save the current monitor configuration to editor-parameters.json.
+ /// This is needed because FancyZones only writes this file when opening the editor or when explicitly requested.
+ ///
+ public static void NotifySaveEditorParameters()
+ {
+ _ = PostMessageW(new IntPtr(0xFFFF), WmPrivSaveEditorParameters, UIntPtr.Zero, IntPtr.Zero);
+ }
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern uint RegisterWindowMessageW(string lpString);