You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Windows服务中SetThreadExecutionState无法阻止系统睡眠/休眠求助

解决Windows服务中PowerRequest API无法阻止系统睡眠的问题

我明白你现在的困扰——控制台里用SetThreadExecutionState好好的,放到Windows服务里就完全失效,哪怕定时调用也没用。咱们先拆解问题根源,再给你落地的解决方案:

问题出在哪?

  1. 会话0隔离限制:Windows服务默认运行在「会话0」环境,这个会话和用户的交互式桌面完全隔离。你代码里的PowerRequestDisplayRequired是针对用户会话的显示休眠阻止请求,在会话0里根本不生效——因为这里没有关联任何用户的显示器。
  2. 重复创建PowerRequest句柄:你每一分钟就重新创建一次请求句柄,这不仅会造成资源泄漏,而且完全没必要。PowerRequest是持久化的,只要句柄有效,设置一次请求就会一直生效,不需要反复调用。

修正后的解决方案

核心调整点

  • 移除PowerRequestDisplayRequired请求:服务不需要管显示器休眠,只需要PowerRequestSystemRequired就能阻止系统进入睡眠/休眠。
  • 只初始化一次PowerRequest:在服务启动时创建句柄并设置请求,停止服务时再清理,不用定时重复操作。
  • 确保服务权限:保持默认的LocalSystem账户运行,它拥有足够权限发起系统级电源请求。

完整修正代码

public partial class Service1 : ServiceBase
{
    private IntPtr _powerRequest; // 持久化保存PowerRequest句柄
    private System.Timers.Timer _heartbeatTimer;

    #region Power Request P/Invokes
    [DllImport("kernel32.dll")]
    static extern IntPtr PowerCreateRequest(ref POWER_REQUEST_CONTEXT Context);

    [DllImport("kernel32.dll")]
    static extern bool PowerSetRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);

    [DllImport("kernel32.dll")]
    static extern bool PowerClearRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
    internal static extern int CloseHandle(IntPtr hObject);

    enum PowerRequestType
    {
        PowerRequestDisplayRequired = 0,
        PowerRequestSystemRequired,
        PowerRequestAwayModeRequired,
        PowerRequestMaximum
    }

    const int POWER_REQUEST_CONTEXT_VERSION = 0;
    const int POWER_REQUEST_CONTEXT_SIMPLE_STRING = 0x1;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct POWER_REQUEST_CONTEXT
    {
        public UInt32 Version;
        public UInt32 Flags;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string SimpleReasonString;
    }
    #endregion

    public Service1()
    {
        InitializeComponent();
        SetupEventLog();
    }

    private void SetupEventLog()
    {
        string source = "Service1";
        string logName = "Application";
        
        if (!EventLog.SourceExists(source))
            EventLog.CreateEventSource(source, logName);
        
        EventLog.WriteEntry(source, "Service initialized", EventLogEntryType.Information);
    }

    protected override void OnStart(string[] args)
    {
        // 初始化电源请求
        var requestContext = new POWER_REQUEST_CONTEXT
        {
            Version = POWER_REQUEST_CONTEXT_VERSION,
            Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING,
            SimpleReasonString = "Continuous measurement service running"
        };

        _powerRequest = PowerCreateRequest(ref requestContext);
        if (_powerRequest != IntPtr.Zero)
        {
            // 仅设置系统级睡眠阻止请求
            bool requestSuccess = PowerSetRequest(_powerRequest, PowerRequestType.PowerRequestSystemRequired);
            EventLog.WriteEntry("Service1", 
                requestSuccess ? "System power request activated" : "Failed to activate system power request",
                requestSuccess ? EventLogEntryType.Information : EventLogEntryType.Error);
        }
        else
        {
            EventLog.WriteEntry("Service1", "Failed to create power request handle", EventLogEntryType.Error);
        }

        // 定时器仅用作心跳日志,无需重复设置电源请求
        _heartbeatTimer = new System.Timers.Timer(60000);
        _heartbeatTimer.Elapsed += OnHeartbeat;
        _heartbeatTimer.Start();
        
        EventLog.WriteEntry("Service1", "Service started successfully", EventLogEntryType.Information);
    }

    private void OnHeartbeat(object sender, System.Timers.ElapsedEventArgs e)
    {
        EventLog.WriteEntry("Service1", "Service is running (heartbeat)", EventLogEntryType.Information);
    }

    protected override void OnStop()
    {
        // 清理电源请求
        if (_powerRequest != IntPtr.Zero)
        {
            PowerClearRequest(_powerRequest, PowerRequestType.PowerRequestSystemRequired);
            CloseHandle(_powerRequest);
            _powerRequest = IntPtr.Zero;
        }

        // 销毁定时器
        _heartbeatTimer?.Stop();
        _heartbeatTimer?.Dispose();
        
        EventLog.WriteEntry("Service1", "Service stopped", EventLogEntryType.Information);
    }
}

额外注意事项

  • 不要勾选服务属性里的「允许服务与桌面交互」:Windows Vista及以后的系统已经禁用了这个功能的实际效果,反而会带来安全隐患。
  • 如果需要阻止系统进入「离开模式」(比如后台持续跑任务),可以额外添加PowerRequestAwayModeRequired请求,同样只需要设置一次即可。
  • 可以通过事件查看器的「应用程序日志」查看服务的运行状态和错误信息,方便排查问题。

内容的提问来源于stack exchange,提问作者Mahesh

火山引擎 最新活动