如何在Windows Service中检测Modern Standby/S0状态?
检测Modern Standby (S0) 状态的有效方案
方法1:通过PowerGetEffectiveOverlayScheme与PowerDeterminePlatformRoleEx API判断
Modern Standby分为Connected Standby和Disconnected Standby两种模式,可通过这两个API组合检测设备是否处于该状态:
C#/.NET 实现代码
using System; using System.Runtime.InteropServices; public static class ModernStandbyDetector { private const int POWER_PLATFORM_ROLE_MOBILE = 2; private const int POWER_PLATFORM_ROLE_TABLET = 8; // Connected/Disconnected Standby的GUID private static readonly Guid GUID_CONNECTED_STANDBY = Guid.Parse("96996BC0-922D-4BB3-AE1E-1E468F6C4500"); private static readonly Guid GUID_DISCONNECTED_STANDBY = Guid.Parse("96996BC1-922D-4BB3-AE1E-1E468F6C4500"); [DllImport("powrprof.dll", SetLastError = true)] private static extern uint PowerDeterminePlatformRoleEx(out int pRole); [DllImport("powrprof.dll", SetLastError = true)] private static extern uint PowerGetEffectiveOverlayScheme(out IntPtr pOverlaySchemeGuid); public static bool IsInModernStandby() { // 先判断设备类型:仅移动/平板设备支持Modern Standby if (PowerDeterminePlatformRoleEx(out int deviceRole) != 0) return false; bool isEligibleDevice = deviceRole == POWER_PLATFORM_ROLE_MOBILE || deviceRole == POWER_PLATFORM_ROLE_TABLET; if (!isEligibleDevice) return false; // 获取当前生效的电源覆盖方案 if (PowerGetEffectiveOverlayScheme(out IntPtr overlayGuidPtr) != 0) return false; try { Guid overlayGuid = Marshal.PtrToStructure<Guid>(overlayGuidPtr); return overlayGuid == GUID_CONNECTED_STANDBY || overlayGuid == GUID_DISCONNECTED_STANDBY; } finally { Marshal.FreeHGlobal(overlayGuidPtr); } } }
应用逻辑
在服务的核心循环或网络操作前调用IsInModernStandby():
- 返回
true时:禁用看门狗、暂停所有网络请求、跳过登录重试逻辑 - 返回
false时:恢复正常服务行为
方法2:监听PowerSettingNotification 电源状态变更通知
通过注册特定电源设置的通知,实时捕获Modern Standby的进入/退出事件:
C#/.NET 实现代码
using System; using System.ServiceProcess; using System.Runtime.InteropServices; public class NetworkService : ServiceBase { private IntPtr _powerNotifyHandle; private const int WM_POWERBROADCAST = 0x0218; private const int PBT_POWERSETTINGCHANGE = 0x8013; private static readonly Guid GUID_SYSTEM_AWAYMODE = Guid.Parse("98a7f580-01f7-48aa-9c0f-44352c29e5C0"); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr RegisterPowerSettingNotification(IntPtr hWnd, ref Guid PowerSettingGuid, uint Flags); [DllImport("user32.dll", SetLastError = true)] private static extern bool UnregisterPowerSettingNotification(IntPtr hPowerNotify); protected override void OnStart(string[] args) { // 注册Modern Standby状态变更通知 _powerNotifyHandle = RegisterPowerSettingNotification(this.ServiceHandle, ref GUID_SYSTEM_AWAYMODE, 0); base.OnStart(args); } protected override void OnStop() { if (_powerNotifyHandle != IntPtr.Zero) { UnregisterPowerSettingNotification(_powerNotifyHandle); } base.OnStop(); } protected override void OnCustomCommand(int command) { if (command == WM_POWERBROADCAST) { bool inStandby = ModernStandbyDetector.IsInModernStandby(); if (inStandby) { DisableWatchdog(); PauseNetworkTasks(); } else { EnableWatchdog(); ResumeNetworkTasks(); // 可选:退出Standby后重新执行登录逻辑 } } base.OnCustomCommand(command); } // 以下为自定义业务逻辑示例 private void DisableWatchdog() => /* 禁用看门狗定时器或终止检测逻辑 */; private void PauseNetworkTasks() => /* 暂停网络请求、关闭Socket连接、停止定时任务 */; private void EnableWatchdog() => /* 恢复看门狗逻辑 */; private void ResumeNetworkTasks() => /* 重启网络任务、重建连接 */; }
关键说明
GUID_SYSTEM_AWAYMODE对应Modern Standby的状态变更事件,设备进入/退出S0时会触发该广播- 结合方法1的主动检测,可避免通知遗漏,确保状态判断准确
注意事项
- 服务需运行在Local System账户下,否则可能无权限调用电源相关API
- 蜂窝网络设备在S0状态下可能保留有限网络连接,但稳定性不可靠,建议统一暂停网络操作
- 需在支持Modern Standby的设备上测试,传统桌面设备不具备该状态
内容的提问来源于stack exchange,提问作者Linus Dillon
相关产品推荐
相关产品推荐

