最近产品有个需求,需要在升级的时候阻止Windows系统进入自动睡眠。需求到手后,小搜了一下,搜到SetThreadExecutionState这个函数,相关的博客挺多,官方文档也挺清晰,想必应该是手拿把掐了,结果没想到连续踩了好几个坑。现在,我就把SetThreadExecutionState的基本使用方法和我踩过的坑整理出来分享给大家。

函数原型

EXECUTION_STATE SetThreadExecutionState(
[in] EXECUTION_STATE esFlags
);

上面就是SetThreadExecutionState的函数原型,在Win32Api里面算是非常简单的函数原型了。入参和返回值都是EXECUTION_STATE类型,入参表示要设置的状态,返回值表示设置前的原有状态,如果失败返回就是0。

EXECUTION_STATE可以是上面这些值或者它们的组合值。从上表可以看出ES_USER_PRESENT不可用,ES_CONTINUOUS用来表示此次设置的状态是否保持有效,所以实际能够设置的状态有三种,下面简单介绍一下每一种的作用。

ES_SYSTEM_REQUIRED (0x00000001)

ES_SYSTEM_REQUIRED的作用是重置系统空闲计时器。系统空闲计时器,或者叫睡眠空闲超时,就是在持续一定时间没有用户输入时,使系统自动进入睡眠或新式待机状态。在Windows11的"系统-电源和电池-使我的设备在以下时间后进入睡眠状态"中可以设置超时时间。

举个例子,假如我设置了在1分钟后使系统进入睡眠,然后我在运行以下代码后就不再对系统进入任何操作

await Task.Delay(30*1000);
SetThreadExecutionState(ES_SYSTEM_REQUIRED);

那么,原本系统会在1分钟后进入睡眠,但是代码在30秒的时候重置了系统空闲计时器,所以实际上系统会在1分30秒后进入睡眠。

如果和ES_CONTINUOUS一起使用,那么会永远阻止系统进入睡眠

SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS);

取消的方法是,再调用一次SetThreadExecutionState,设置ES_CONTINUOUS并且不设置ES_SYSTEM_REQUIRED,即

SetThreadExecutionState(ES_CONTINUOUS);

ES_DISPLAY_REQUIRED (0x00000002)

ES_DISPLAY_REQUIRED的作用是重置显示空闲计时器。显示空闲计时器,或者叫显示空闲超时,对应的是Windows11的"系统-电源和电池-在此时间后关闭我的屏幕"设置。ES_SYSTEM_REQUIRED的使用方式和ES_SYSTEM_REQUIRED相同,不再举例。

那么有一个问题,假如我把睡眠空闲超时和显示空闲超时都设置为1分钟,显然,在系统空闲1分钟后,屏幕会关闭,系统也会进入睡眠。

如果我的应用程序在30秒的时候,仅重置了睡眠空闲超时,那么1分钟后屏幕会关闭,但系统会继续运行,到1分30秒的时候进入睡眠。

如果我的应用程序在30秒的时候,仅重置了显示空闲超时会怎么样呢?我在Win11 24H2在做了验证,结果是在1分钟的时候,屏幕没有关闭,系统也没有进入睡眠,在1分30秒时,屏幕关闭和系统睡眠同时发生。可见,在Win11 24H2中,不存在一种系统睡眠而屏幕未关闭的状态,系统需要关闭屏幕后才能进入睡眠。

ES_AWAYMODE_REQUIRED (0x00000040)

使用ES_SYSTEM_REQUIRED可以防止系统自动进入睡眠,那如果用户手动点击睡眠,或者其他应用程序调用API使系统进入睡眠,有没有办法阻止这一行为呢?

我们知道,当我们点击开始菜单的睡眠按钮时,屏幕会关闭,系统会进入睡眠,应用程序会被挂起,例如,使用下面的代码,每隔1秒钟,打印一次当前时间

while(true)
{
Console.WriteLine($"{DateTime.Now:T}");
await Task.Delay(1000);
}

在代码运行中,点击系统开始菜单的睡眠,过一会儿再唤醒系统,可以看到,代码的输出结果为:

//......
12:39:24
12:39:25
12:39:36
12:39:37
//......

可以看到中间有10几秒的间隔没有输出,这说明系统睡眠时代码被挂起了,系统唤醒后代码才继续执行。

要想我们的代码能够在系统睡眠时在后台继续执行,可以使用ES_AWAYMODE_REQUIRED。如果我们把代码改成这样,然后执行相同的操作,那么我们仍然会得到时间连续的输出,因为我们的代码没有被挂起,而是在后台继续执行。

SetThreadExecutionState(ES_AWAYMODE_REQUIRED | ES_CONTINUOUS);
while(true)
{
Console.WriteLine($"{DateTime.Now:T}");
await Task.Delay(1000);
}

ES_AWAYMODE_REQUIRED和ES_SYSTEM_REQUIRED的区别是,ES_AWAYMODE_REQUIRED不会阻止系统进入睡眠,但系统也没有完全睡眠,因为我们的代码仍在运行。在这个状态中,其他没有设置ES_AWAYMODE_REQUIRED的应用会被系统挂起。

小节

  • ES_SYSTEM_REQUIRED用来阻住自动睡眠,ES_DISPLAY_REQUIRED用来阻止屏幕自动关闭,ES_AWAYMODE_REQUIRED使应用程序在睡眠时能够在后台执行。
  • 不带ES_CONTINUOUS是单次请求,重置一次超时计时;带上ES_CONTINUOUS是持续请求,单独设置ES_CONTINUOUS可取消持续请求。
  • ES_AWAYMODE_REQUIRED只适用持续请求。

在多线程中的使用

虽然站在用户的角度来看,是应用程序阻止了睡眠,但SetThreadExecutionState这个函数,如它的函数名称一样,它设置的是线程的执行请求(电源请求)。

进程的电源请求状态,是所有线程的电源请求状态的并集。下面的代码在三个线程中分别设置了ES_DISPLAY_REQUIRED、ES_SYSTEM_REQUIRED和ES_AWAYMODE_REQUIRED三种持续的电源请求:

var starter1 = new ThreadStart(()=>
{
SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
Thread.Sleep(30000);
}); var starter2 = new ThreadStart(()=>
{
SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
Thread.Sleep(30000);
}); var starter3 = new ThreadStart(()=>
{
SetThreadExecutionState(EXECUTION_STATE.ES_AWAYMODE_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
Thread.Sleep(30000);
}); var t1 = new Thread(starter1);
t1.Start(); var t2 = new Thread(starter2);
t2.Start(); var t3 = new Thread(starter3);
t3.Start();

运行后,我们在控制台使用powercfg /requests命令来查询当前设备上应用程序的电源请求,结果如下:

结果是AwakeDemo-Console.exe这个进程同时具有三种电源请求。

在一个线程中设置的电源请求,只能在该线程中取消,或者等待线程终止后,电源请求自动取消。

服务进程中无法设置ES_DISPLAY_REQUIRED

话不多说,直接上示例演示:

首先用VS的Windows服务模板新建一个项目

然后在Service1.cs中添加以下代码,使服务在启动时同时设置DISPLAY、SYSTEM和AWAYMODE请求

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); [FlagsAttribute]
public enum EXECUTION_STATE : uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001
} protected override void OnStart(string[] args)
{
SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_AWAYMODE_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}

编译后找到输出的exe文件,在cmd中输入以下命令创建和运行服务

//创建一个名为awake-demo的服务,二进制文件是指定的exe文件的路径
sc create awake-demo binPath= "D:\Projects\CSharp Projects\AwakeDemo\AwakeDemo\bin\Release\AwakeDemo.exe" //启动awake-demo服务
sc start awake-demo

然后用powercfg /requests查询,结果如下:

可以看到,服务进程只设置了SYSTEM和AWAYMODE请求。通过简单验证可以发现,确实只有SYSTEM和AWAYMODE请求生效了。

Tips

本文展示的结果都来自于支持新式待机(Modern Standby)的移动设备,在不支持新式待机的传统设备上,表现可能会有差异。

如何正确使用SetThreadExecutionState来阻止Windows进入睡眠的更多相关文章

  1. 阻止系统自动睡眠的小软件,附C#制作过程(执行SetThreadExecutionState API函数,让系统误判)

    因为有时下载东西的时候,不想让电脑自动深入睡眠,所以就开启了离开模式.这样不但不节能环保,而且到真正想要睡眠的时候就是一翻蛋疼. 改过自新,关闭了离开模式,同时无操作30分钟后也会进入睡眠模式.但是在 ...

  2. 阻止系统自动睡眠的小软件,附C#制作过程

    原文 http://www.cnblogs.com/h46incon/p/SleepPreventer.html 因为有时下载东西的时候,不想让电脑自动深入睡眠,所以就开启了离开模式.这样不但不节能环 ...

  3. .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态

    原文:.NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态 在 Windows 系统中,一段时间不操作键盘和鼠标,屏幕便会关闭,系统会进入睡眠状态.但有些程序(比如游戏.视频和演示文稿)在运行过程中应该 ...

  4. MFC: 获得关机消息;阻止Windows关机

    WM_QUERYENDSESSION消息是Windows向你询问Windows能否关闭,WM_ENDSESSION消息表示提示你Windows即将关闭.故当应用程序退出时, WM_QUERYENDSE ...

  5. 正确把mysql数据库从windows迁移到linux系统上的方法

    (一)用mysqldump命令导出数据库文件: 在windows下cd到Mysql的bin目录: c:/data.txt这个目录和导出的文本名可以自己随便取,-B 后面的是表名,我要导出的表明叫use ...

  6. 桌面程序阻止Windows关机(使用Message.Result取得DefWindowProc API函数的返回值,非常重要)

    Windows Client 客户端在关机,不外乎两种情况: 1. 没有处理 Windows 关机消息: 2.处理了关机消息,但是超时了: 上面这两种情况,都会让Windows 关不了机.在现实生活中 ...

  7. 如何找出阻止windows睡眠的原因或软件

    1.开始菜单 2.搜索程序和文件里输入 CMD 3.cmd.exe上右键点击以管理员权限运行 4.在cmd黑屏窗口里输入 powercfg -requests如下图所示

  8. windows系统下npm升级的正确姿势以及原理

    本文来自网易云社区 作者:陈观喜 网上关于npm升级很多方法多种多样,但是在windows系统下不是每种方法都会正确升级.其中在windows系统下主要的升级方法有以下三种: 首先最暴力的方法删掉no ...

  9. Windows里面的hosts文件

    一.什么是Hosts文件? hosts文件是一个用于储存计算机网络中各节点信息的计算机文件.这个文件负责将主机名映射到相应的IP地址.hosts文件通常用于补充或取代网络中DNS的功能.和DNS不同的 ...

  10. windows 10 超级优化提速 附系统服务列表纯净

    如图,本机安装了vs2017 office2016 迅雷.谷歌浏览器,不建议安装其它任何软件.vs2017为开发软件,用于编程,一般用户用不到. 如果想安装其它的软件,建议优先使用绿色版本的. 下载服 ...

随机推荐

  1. 把多个文件打包压缩成tar.gz文件并解压的Java实现

    压缩文件   在Java中,可以 使用GZIPOutputStream创建gzip(gz)压缩文件,它在commons-compress下面,可以通过如下的maven坐标引入: <depende ...

  2. Ubuntu部署tensorflow(CPU/GPU)方法

      本文介绍在Linux操作系统的发行版本Ubuntu中,配置可以用CPU或GPU运行的Python新版本深度学习库tensorflow的方法.   在文章部署CPU与GPU通用的tensorflow ...

  3. code-generator的简单介绍

    代码生成器 介绍 client-go为每种k8s内置资源提供了对应的clientset和informer.那么我们要监听和操作自定义资源对象,应该如何做呢? 方式一:使用client-go提供的dyn ...

  4. 基于Fastapi的区分聊天房间的聊天转发功能接口示例

    基于房间码(eCode)和用户uid,区分不同的聊天房间进行消息转发. 前端将收到的消息根据房间码(eCode)过滤到不同的聊天记录显示页面 后端demo代码如下: from fastapi impo ...

  5. GPU开启持久化模式

    GPU开启持久化模式 GPU驱动内存常驻模式,也称为GPU驱动持久模式.linux 系统下,在 persistence 模式是 enabled 状态时, GPU 驱动一直处于加载状态, 减少运行程序时 ...

  6. hot100之回溯下

    单词搜索(079) class Solution { int m, n; public boolean exist(char[][] board, String word) { m = board.l ...

  7. 探索 JavaCV:开启计算机视觉与多媒体处理新世界

    目录 JavaCV 是什么? 安装指南 有趣的 JavaCV 使用示例 录制 RTMP 直播流 捕获摄像头画面 美颜相机 引用 在当今的技术领域,计算机视觉和多媒体处理的应用愈发广泛.从视频监控到直播 ...

  8. Java学习篇(四)—— Java 多线程

    如何创建一个线程? Java创建线程有两种方法,这里对三种方法做一个梳理,方便理解. 实现Runnable接口和run()方法 Java的接口就是一种协议,约定了想要被统一管理的类要遵循的协议.在Ja ...

  9. input添加文字 提示效果 点击后清空,移出时恢复提示

    <input type="text" value="模糊型号查询" onfocus="if(value=='模糊型号查询') {value='' ...

  10. C# WinForm NumericUpDown 控件全选其中文字 (Numeric 全选文本) 全选文本Numeric

    num_length.Focus();                    UpDownBase updbText = (UpDownBase)num_length;                 ...