当我的程序运行时,如何防止Windows进入睡眠状态?

43

当我的程序在运行时,我需要阻止Windows进入睡眠状态。

我不仅想要防止睡眠计时器,我还希望在按下睡眠按钮或以任何其他方式主动让计算机进入睡眠状态时取消睡眠事件。因此,SetThreadExecutionState是不够的。

或者...我实际上不需要完全阻止睡眠,只需要延迟5-10秒以允许我的程序完成任务。

(我知道这是不好的程序行为,但这只是用于个人使用。)

8个回答

27

在考虑了vim的答案之后

"使用PowerCreateRequest、PowerSetRequest和PowerClearRequest函数是首选方法。"

通过链接到msdn上的AvailabilityRequests.docx,这个文件太过详细(需要阅读太多),我在网上搜索了一个基于PowerCreateRequest的具体的示例,并发现了http://go4answers.webhost4life.com/Example/problem-monitor-wakeup-service-windows7-12092.aspx [编辑2016 - 不再可用]

我将其复制并调整以满足我的需求(从msdn复制的CloseHandle的PInvoke):

using System.Runtime.InteropServices;

    #region prevent screensaver, display dimming and automatically sleeping
    POWER_REQUEST_CONTEXT _PowerRequestContext;
    IntPtr _PowerRequest; //HANDLE

    // Availability Request Functions
    [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);

    // Availablity Request Enumerations and Constants
    enum PowerRequestType
    {
        PowerRequestDisplayRequired = 0,
        PowerRequestSystemRequired,
        PowerRequestAwayModeRequired,
        PowerRequestMaximum
    }

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

    // Availablity Request Structures
    // Note:  Windows defines the POWER_REQUEST_CONTEXT structure with an
    // internal union of SimpleReasonString and Detailed information.
    // To avoid runtime interop issues, this version of 
    // POWER_REQUEST_CONTEXT only supports SimpleReasonString.  
    // To use the detailed information,
    // define the PowerCreateRequest function with the first 
    // parameter of type POWER_REQUEST_CONTEXT_DETAILED.
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct POWER_REQUEST_CONTEXT
    {
        public UInt32 Version;
        public UInt32 Flags;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string
            SimpleReasonString;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PowerRequestContextDetailedInformation
    {
        public IntPtr LocalizedReasonModule;
        public UInt32 LocalizedReasonId;
        public UInt32 ReasonStringCount;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string[] ReasonStrings;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct POWER_REQUEST_CONTEXT_DETAILED
    {
        public UInt32 Version;
        public UInt32 Flags;
        public PowerRequestContextDetailedInformation DetailedInformation;
    }
    #endregion



    /// <summary>
    /// Prevent screensaver, display dimming and power saving. This function wraps PInvokes on Win32 API. 
    /// </summary>
    /// <param name="enableConstantDisplayAndPower">True to get a constant display and power - False to clear the settings</param>
    private void EnableConstantDisplayAndPower(bool enableConstantDisplayAndPower)
    {
        if (enableConstantDisplayAndPower)
        {
            // Set up the diagnostic string
            _PowerRequestContext.Version = POWER_REQUEST_CONTEXT_VERSION;
            _PowerRequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
            _PowerRequestContext.SimpleReasonString = "Continuous measurement"; // your reason for changing the power settings;

            // Create the request, get a handle
            _PowerRequest = PowerCreateRequest(ref _PowerRequestContext);

            // Set the request
            PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
            PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);
        }
        else
        {
            // Clear the request
            PowerClearRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
            PowerClearRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);

            CloseHandle(_PowerRequest);
        }
    }

8
这种方法很好,因为你提供的系统无法进入睡眠等原因将在运行powercfg /requests命令时显示出来。这将有助于用户诊断睡眠问题。 - Jack Ukleja
4
讽刺的是,一个名为webhost4life的网站已经关闭。 - Joshua Frank
POWER_REQUEST_CONTEXT 结构体应该在末尾真正有两个额外的 IntPtr 字段来填充它,这样当详细情况时,结构体的大小就会达到最小值。 - John Hall

23

我曾经遇到过一个通过 USB 连接的硬件设备的问题。XP/Vista 会在中途进入睡眠/休眠状态...你可能会说,它恢复后可以继续工作。但是如果硬件仍然连接着呢?!

用户有随时拔掉电缆的习惯。

你需要处理 XP 和 Vista。

在 XP 下,捕获 WM_POWERBROADCAST 并查找 PBT_APMQUERYSUSPEND wparam。

   // See if bit 1 is set, this means that you can send a deny while we are busy
   if (message.LParam & 0x1)
   {
      // send the deny message
      return BROADCAST_QUERY_DENY;
   } // if
   else
   {
      return TRUE;
   } // else

在Vista下使用SetThreadExecutionState,像这样

// try this for vista, it will fail on XP
if (SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED) == NULL)
{
   // try XP variant as well just to make sure 
   SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
}  // if 

当你的应用程序完成后,将其设置回正常状态。

// set state back to normal
SetThreadExecutionState(ES_CONTINUOUS);

1
哦,我错了,SetThreadExecutionState确实起作用了,只是需要设置ES_AWAYMODE_REQUIRED。 奇怪的是我的显示器会变黑,但系统从未完全进入睡眠状态。 - ping
2
这就是离线模式的全部内容。其想法是计算机通常会处于睡眠状态,因此它被制作成看起来像是在睡觉。然后,当后台任务完成时(例如录制电视节目),应用程序关闭系统所需和离线模式位,计算机实际上进入睡眠状态。 - Edward Brey
2
这是一个链接,可以帮助大家查找窗口消息:http://msdn.microsoft.com/zh-cn/library/windows/desktop/aa372716(v=vs.85).aspx - chaz
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); 这个函数在WinXP上能用吗? - Noitidart
我仍然不清楚ES_AWAYMODE_REQUIRED和ES_SYSTEM_REQUIRED有何不同。 - Alex Budovski
显示剩余2条评论

10

2

设置wsc = CreateObject("WScript.Shell")

做 WScript.Sleep (60*1000) wsc.SendKeys ("{SCROLLLOCK 2}") 循环

-将上述代码放入记事本并将文件保存为.vbs,然后双击该文件即可。


1
如果您需要一个显示器在应用程序运行期间一直工作,那么请尝试设置 "ES_DISPLAY_REQUIRED" 而不是离开模式:
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);

完成应用程序后,请确保清除所有其他标志。

SetThreadExecutionState(ES_CONTINUOUS);

1

0

不可能,我必须在电脑进入睡眠状态之前禁用WiFi设备。否则,当我再次唤醒电脑时,该设备将变得无法使用。英特尔的Win7驱动程序速度很慢:( - ping

0
以下是我使用现代电源可用性请求API(取代了SetThreadExecutionState)的尝试,如vim建议的
我正在使用一个很好的P/Invoke NuGet,Vanara.PInvoke.Kernel32
using Vanara.PInvoke;
using static Vanara.PInvoke.Kernel32;

// create request object
using var request = PowerCreateRequest(new REASON_CONTEXT("App FOO is working"));
if (request.IsInvalid)
{
    throw new InvalidOperationException(
       $"Could not create power availability request: {Win32Error.GetLastError()}");
}

// send request
if (!PowerSetRequest(request, POWER_REQUEST_TYPE.PowerRequestSystemRequired))
{
    throw new InvalidOperationException(
       $"Could not send power availability request: {Win32Error.GetLastError()}");
}
    
// do stuff that required the machine to be up
Console.WriteLine("Doing stuff...");
await Task.Delay(5000);

// clear request
if (!PowerClearRequest(request, POWER_REQUEST_TYPE.PowerRequestSystemRequired))
{
    Console.WriteLine(
      "WARNING: Could not clear power availability request: {0}",
      Win32Error.GetLastError());
}

您可以通过在管理员终端中输入powercfg /requests来查看您的请求执行情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接