From f239866c6811792f60da0fd243ffd003a1e609c4 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 30 Sep 2025 21:11:03 +0800
Subject: [PATCH] Use sleep mode listener to fix modern standby sleep mode
issue
---
.../NativeMethods.txt | 7 +-
Flow.Launcher.Infrastructure/Win32Helper.cs | 104 +++++++++++++++++-
Flow.Launcher/MainWindow.xaml.cs | 53 +++++++--
3 files changed, 150 insertions(+), 14 deletions(-)
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
index eb844dd7c..cd072f635 100644
--- a/Flow.Launcher.Infrastructure/NativeMethods.txt
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -85,5 +85,10 @@ QueryFullProcessImageName
EVENT_OBJECT_HIDE
EVENT_SYSTEM_DIALOGEND
+DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
WM_POWERBROADCAST
-PBT_APMRESUMEAUTOMATIC
\ No newline at end of file
+PBT_APMRESUMEAUTOMATIC
+PBT_APMRESUMESUSPEND
+PowerRegisterSuspendResumeNotification
+PowerUnregisterSuspendResumeNotification
+DeviceNotifyCallbackRoutine
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 5d30b740d..c94008b03 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -19,6 +19,7 @@ using Microsoft.Win32.SafeHandles;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
+using Windows.Win32.System.Power;
using Windows.Win32.System.Threading;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.Shell.Common;
@@ -338,9 +339,6 @@ namespace Flow.Launcher.Infrastructure
public const int SC_MAXIMIZE = (int)PInvoke.SC_MAXIMIZE;
public const int SC_MINIMIZE = (int)PInvoke.SC_MINIMIZE;
- public const int WM_POWERBROADCAST = (int)PInvoke.WM_POWERBROADCAST;
- public const int PBT_APMRESUMEAUTOMATIC = (int)PInvoke.PBT_APMRESUMEAUTOMATIC;
-
#endregion
#region Window Handle
@@ -918,5 +916,105 @@ namespace Flow.Launcher.Infrastructure
}
#endregion
+
+ #region Sleep Mode Listener
+
+ private static Action _func;
+ private static PDEVICE_NOTIFY_CALLBACK_ROUTINE _callback = null;
+ private static DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS _recipient;
+ private static SafeHandle _recipientHandle;
+ private static HPOWERNOTIFY _handle = HPOWERNOTIFY.Null;
+
+ ///
+ /// Registers a listener for sleep mode events.
+ /// Inspired from: https://github.com/XKaguya/LenovoLegionToolkit
+ /// https://blog.csdn.net/mochounv/article/details/114668594
+ ///
+ ///
+ ///
+ public static unsafe void RegisterSleepModeListener(Action func)
+ {
+ if (_callback != null)
+ {
+ // Only register if not already registered
+ return;
+ }
+
+ _func = func;
+ _callback = new PDEVICE_NOTIFY_CALLBACK_ROUTINE(DeviceNotifyCallback);
+ _recipient = new DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS()
+ {
+ Callback = _callback,
+ Context = null
+ };
+
+ _recipientHandle = new StructSafeHandle(_recipient);
+ _handle = PInvoke.PowerRegisterSuspendResumeNotification(
+ REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_CALLBACK,
+ _recipientHandle,
+ out var handle) == WIN32_ERROR.ERROR_SUCCESS ?
+ new HPOWERNOTIFY(new IntPtr(handle)) :
+ HPOWERNOTIFY.Null;
+ if (_handle.IsNull)
+ {
+ throw new Win32Exception("Error registering for power notifications: " + Marshal.GetLastWin32Error());
+ }
+ }
+
+ ///
+ /// Unregisters the sleep mode listener.
+ ///
+ public static void UnregisterSleepModeListener()
+ {
+ if (!_handle.IsNull)
+ {
+ PInvoke.PowerUnregisterSuspendResumeNotification(_handle);
+ _handle = HPOWERNOTIFY.Null;
+ _func = null;
+ _callback = null;
+ _recipientHandle = null;
+ }
+ }
+
+ private static unsafe uint DeviceNotifyCallback(void* context, uint type, void* setting)
+ {
+ switch (type)
+ {
+ case PInvoke.PBT_APMRESUMEAUTOMATIC:
+ // Operation is resuming automatically from a low-power state.This message is sent every time the system resumes
+ _func();
+ break;
+
+ case PInvoke.PBT_APMRESUMESUSPEND:
+ // Operation is resuming from a low-power state.This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key
+ _func();
+ break;
+ }
+
+ return 0;
+ }
+
+ private sealed class StructSafeHandle : SafeHandle where T : struct
+ {
+ private readonly nint _ptr = nint.Zero;
+
+ public StructSafeHandle(T recipient) : base(nint.Zero, true)
+ {
+ var pRecipient = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(recipient, pRecipient, false);
+ SetHandle(pRecipient);
+ _ptr = pRecipient;
+ }
+
+ public override bool IsInvalid => handle == nint.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Marshal.FreeHGlobal(_ptr);
+ return true;
+ }
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index a9e03bc8c..01a7dc9bd 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -95,6 +95,7 @@ namespace Flow.Launcher
UpdatePosition();
InitSoundEffects();
+ RegisterSoundEffectsEvent();
DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
_viewModel.ActualApplicationThemeChanged += ViewModel_ActualApplicationThemeChanged;
}
@@ -668,16 +669,6 @@ namespace Flow.Launcher
handled = true;
}
break;
- case Win32Helper.WM_POWERBROADCAST: // Handle power broadcast messages
- // https://learn.microsoft.com/en-us/windows/win32/power/wm-powerbroadcast
- if (wParam.ToInt32() == Win32Helper.PBT_APMRESUMEAUTOMATIC)
- {
- // Fix for sound not playing after sleep / hibernate
- // https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
- InitSoundEffects();
- }
- handled = true;
- break;
}
return IntPtr.Zero;
@@ -723,6 +714,47 @@ namespace Flow.Launcher
}
}
+ private void RegisterSoundEffectsEvent()
+ {
+ // Fix for sound not playing after sleep / hibernate for both modern standby and legacy standby
+ // https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
+ try
+ {
+ Win32Helper.RegisterSleepModeListener(() =>
+ {
+ if (Application.Current == null)
+ {
+ return;
+ }
+
+ // We must run InitSoundEffects on UI thread because MediaPlayer is a DispatcherObject
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.Invoke(InitSoundEffects);
+ return;
+ }
+
+ InitSoundEffects();
+ });
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, "Failed to register sound effect event", e);
+ }
+ }
+
+ private static void UnregisterSoundEffectsEvent()
+ {
+ try
+ {
+ Win32Helper.UnregisterSleepModeListener();
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, "Failed to unregister sound effect event", e);
+ }
+ }
+
#endregion
#region Window Notify Icon
@@ -1447,6 +1479,7 @@ namespace Flow.Launcher
_animationSoundWMP?.Close();
_animationSoundWPF?.Dispose();
_viewModel.ActualApplicationThemeChanged -= ViewModel_ActualApplicationThemeChanged;
+ UnregisterSoundEffectsEvent();
}
_disposed = true;