C#中如何阻止硬件休眠
在 C# 中阻止系统进入休眠状态(包括显示关闭和系统休眠)需要使用 Windows API。以下是完整的实现方案,包含多种控制级别和实际应用场景:
核心解决方案:使用 SetThreadExecutionState API
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class SleepPreventer : IDisposable
{
// 导入 Windows API
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern uint SetThreadExecutionState(uint esFlags);
// 执行状态标志
private const uint ES_CONTINUOUS = 0x80000000;
private const uint ES_SYSTEM_REQUIRED = 0x00000001;
private const uint ES_DISPLAY_REQUIRED = 0x00000002;
private const uint ES_AWAYMODE_REQUIRED = 0x00000040;
private uint _previousState;
private bool _isDisposed;
/// <summary>
/// 初始化并阻止系统休眠
/// </summary>
/// <param name="preventDisplaySleep">是否阻止显示器关闭</param>
/// <param name="preventSystemSleep">是否阻止系统休眠</param>
public SleepPreventer(bool preventDisplaySleep = true, bool preventSystemSleep = true)
{
uint newState = ES_CONTINUOUS;
if (preventSystemSleep)
{
newState |= ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED;
}
if (preventDisplaySleep)
{
newState |= ES_DISPLAY_REQUIRED;
}
// 设置新状态并保存之前的状态
_previousState = SetThreadExecutionState(newState);
if (_previousState == 0)
{
throw new Exception("设置执行状态失败: " + Marshal.GetLastWin32Error());
}
}
/// <summary>
/// 恢复原始状态(允许系统休眠)
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
// 恢复之前的执行状态
if (_previousState != 0)
{
SetThreadExecutionState(_previousState);
}
_isDisposed = true;
}
}
~SleepPreventer()
{
Dispose(false);
}
/// <summary>
/// 临时重置休眠计时器(推荐用于长时间操作)
/// </summary>
public static void ResetSleepTimer(bool resetDisplay = true)
{
uint flags = ES_SYSTEM_REQUIRED;
if (resetDisplay) flags |= ES_DISPLAY_REQUIRED;
SetThreadExecutionState(flags);
}
}
使用示例
场景 1:长时间操作期间阻止休眠(如文件传输)
// 开始长时间操作
using (var sleepLock = new SleepPreventer())
{
// 执行耗时操作(如大文件传输)
TransferLargeFile("source.zip", "destination.zip");
// 在循环中定期重置计时器
for (int i = 0; i < 100; i++)
{
DoWorkChunk(i);
SleepPreventer.ResetSleepTimer(); // 每部分完成时重置计时器
}
} // 离开 using 范围后自动允许休眠
场景 2:媒体播放期间保持唤醒
public class MediaPlayer : IDisposable
{
private SleepPreventer _sleepPreventer;
public void Play()
{
// 播放开始时阻止休眠
_sleepPreventer = new SleepPreventer(preventDisplaySleep: true);
// 开始播放...
}
public void Pause()
{
// 暂停时允许休眠
_sleepPreventer?.Dispose();
_sleepPreventer = null;
}
public void Dispose()
{
_sleepPreventer?.Dispose();
}
}
场景 3:后台服务保持系统唤醒
public class BackgroundService
{
private SleepPreventer _sleepPreventer;
public void StartMonitoring()
{
// 阻止系统休眠但允许显示器关闭
_sleepPreventer = new SleepPreventer(preventDisplaySleep: false,
preventSystemSleep: true);
// 开始监控...
}
public void Stop()
{
_sleepPreventer?.Dispose();
}
}
高级控制:使用 Windows 电源请求
对于更复杂的场景(需要系统完全保持唤醒状态),可以使用 PowerCreateRequest API:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr PowerCreateRequest(ref POWER_REQUEST_CONTEXT context);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool PowerSetRequest(IntPtr powerRequest, POWER_REQUEST_TYPE requestType);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool PowerClearRequest(IntPtr powerRequest, POWER_REQUEST_TYPE requestType);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct POWER_REQUEST_CONTEXT
{
public uint Version;
public uint Flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string SimpleReasonString;
}
private enum POWER_REQUEST_TYPE
{
PowerRequestDisplayRequired,
PowerRequestSystemRequired,
PowerRequestAwayModeRequired
}
public class PowerRequest : IDisposable
{
private IntPtr _handle;
private bool _isDisposed;
public PowerRequest(string reason)
{
var context = new POWER_REQUEST_CONTEXT
{
Version = 0,
Flags = 0,
SimpleReasonString = reason
};
_handle = PowerCreateRequest(ref context);
if (_handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!PowerSetRequest(_handle, POWER_REQUEST_TYPE.PowerRequestSystemRequired))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public void Dispose()
{
if (!_isDisposed)
{
if (_handle != IntPtr.Zero)
{
PowerClearRequest(_handle, POWER_REQUEST_TYPE.PowerRequestSystemRequired);
CloseHandle(_handle);
_handle = IntPtr.Zero;
}
_isDisposed = true;
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
}
实际应用技巧
最小化影响:
// 仅在必要时阻止休眠
if (operationWillTakeMoreThan(TimeSpan.FromMinutes(10)))
{
using (var preventer = new SleepPreventer())
{
ExecuteLongOperation();
}
}
用户配置选项:
// 在设置中添加选项
if (Settings.PreventSleepDuringOperations)
{
SleepPreventer.ResetSleepTimer();
}
处理系统电源事件:
Microsoft.Win32.SystemEvents.PowerModeChanged += (s, e) =>
{
if (e.Mode == PowerModes.Suspend)
{
// 系统即将休眠,保存状态
SaveCurrentState();
}
};
多显示器场景:
// 检测是否有外接显示器
bool hasExternalDisplay = Screen.AllScreens.Length > 1;
// 仅在无外接显示器时阻止显示器关闭
using (var preventer = new SleepPreventer(
preventDisplaySleep: !hasExternalDisplay))
{
// 执行操作...
}
超时处理
// 设置最大阻止时间
using (var timeout = new System.Threading.Timer(_ =>
{
preventer?.Dispose();
}, null, TimeSpan.FromHours(2), Timeout.InfiniteTimeSpan))
{
using (var preventer = new SleepPreventer())
{
// 长时间操作...
}
}
其他使用
public class SystemSleepAPI
{
//定义API函数
[DllImport("kernel32.dll")]
static extern uint SetThreadExecutionState(ExecutionFlag flags);
[Flags]
enum ExecutionFlag : uint
{
System = 0x00000001,
Display = 0x00000002,
Continus = 0x80000000,
}
/// <summary>
///阻止系统休眠,直到线程结束恢复休眠策略
/// </summary>
/// <param name="includeDisplay">是否阻止关闭显示器</param>
public static void PreventSleep(bool includeDisplay = false)
{
try
{
if (includeDisplay)
SetThreadExecutionState(ExecutionFlag.System | ExecutionFlag.Display | ExecutionFlag.Continus);
else
SetThreadExecutionState(ExecutionFlag.System | ExecutionFlag.Continus);
}
catch { }
}
/// <summary>
///恢复系统休眠策略
/// </summary>
public static void ResotreSleep()
{
try { SetThreadExecutionState(ExecutionFlag.Continus); } catch { }
}
/// <summary>
///重置系统休眠计时器
/// </summary>
/// <param name="includeDisplay">是否阻止关闭显示器</param>
public static void ResetSleepTimer(bool includeDisplay = false)
{
try
{
if (includeDisplay)
SetThreadExecutionState(ExecutionFlag.System | ExecutionFlag.Display);
else
SetThreadExecutionState(ExecutionFlag.System);
}
catch { }
}
}
使用实例
void time()
{
PreventSleep(true);
}