有任何API可以阻止Windows 8进入连接待机模式

Any API to prevent Windows 8 from going into connected standby mode?

本文关键字:连接 模式 待机 Windows API 任何      更新时间:2023-10-16

在桌面应用程序完成之前,我需要禁用连接的待机模式。所需的行为应该类似于我通过远程桌面连接到该机器时发生的情况。也就是说,屏幕关闭了,但直到我断开连接,系统才会进入睡眠状态。

对于我的应用程序,是否有任何记录或未记录的方法可以获得相同的行为?

我尝试了PowerSetRequestPowerRequestExecutionRequired和/或PowerRequestAwayModeRequired,但系统仍在几分钟后进入连接待机模式。我目前使用PowerRequestDisplayRequired来保持它的活力,但屏幕始终保持打开状态。

已编辑这是测试应用程序。按下硬件电源按钮后,计时器滴答作响的时间不超过5分钟,屏幕关闭(使用电池运行(。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CsTestApp
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            this.Load += MainForm_Load;
        }
        void MainForm_Load(object sender, EventArgs e)
        {
            // init timer
            var timer = new System.Windows.Forms.Timer();
            timer.Interval = 1000;
            timer.Tick += delegate 
            { 
                System.Diagnostics.Trace.WriteLine("CsTestApp: " + DateTime.Now); 
            };
            timer.Start();
            // set GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT
            IntPtr pActiveSchemeGuid;
            var hr = PowerGetActiveScheme(IntPtr.Zero, out pActiveSchemeGuid);
            if (hr != 0)
                Marshal.ThrowExceptionForHR((int)hr);
            Guid activeSchemeGuid = (Guid)Marshal.PtrToStructure(pActiveSchemeGuid, typeof(Guid));
            LocalFree(pActiveSchemeGuid);
            int savedTimeout;
            hr = PowerReadDCValueIndex(
                IntPtr.Zero,
                activeSchemeGuid,
                GUID_IDLE_RESILIENCY_SUBGROUP,
                GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT,
                out savedTimeout);
            if (hr != 0)
                Marshal.ThrowExceptionForHR((int)hr);
            hr = PowerWriteDCValueIndex(
                IntPtr.Zero,
                activeSchemeGuid,
                GUID_IDLE_RESILIENCY_SUBGROUP,
                GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT,
                -1);
            if (hr != 0)
                Marshal.ThrowExceptionForHR((int)hr);
            // create power request
            var powerRequestContext = new POWER_REQUEST_CONTEXT();
            powerRequestContext.Version = POWER_REQUEST_CONTEXT_VERSION;
            powerRequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
            powerRequestContext.SimpleReasonString = "Disable Connected Standby";
            var powerRequest = PowerCreateRequest(ref powerRequestContext);
            if (powerRequest == IntPtr.Zero)
                ThrowLastWin32Error();
            // set PowerRequestExecutionRequired
            if (!PowerSetRequest(powerRequest, PowerRequestType.PowerRequestExecutionRequired))
                ThrowLastWin32Error();
            this.FormClosed += delegate
            {
                timer.Dispose();
                PowerClearRequest(powerRequest, PowerRequestType.PowerRequestExecutionRequired);
                CloseHandle(powerRequest);
                hr = PowerWriteDCValueIndex(
                    IntPtr.Zero,
                    activeSchemeGuid,
                    GUID_IDLE_RESILIENCY_SUBGROUP,
                    GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT,
                    savedTimeout);
                if (hr != 0)
                    Marshal.ThrowExceptionForHR((int)hr);
            };
        }

        // power API interop
        static void ThrowLastWin32Error()
        {
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
        }
        enum PowerRequestType
        {
            PowerRequestDisplayRequired = 0,
            PowerRequestSystemRequired = 1,
            PowerRequestAwayModeRequired = 2,
            PowerRequestExecutionRequired = 3,
            PowerRequestMaximum
        }
        [StructLayout(LayoutKind.Sequential)]
        struct PowerRequestContextDetailedInformation
        {
            public IntPtr LocalizedReasonModule;
            public UInt32 LocalizedReasonId;
            public UInt32 ReasonStringCount;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string[] ReasonStrings;
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct POWER_REQUEST_CONTEXT_DETAILED
        {
            public UInt32 Version;
            public UInt32 Flags;
            public PowerRequestContextDetailedInformation DetailedInformation;
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct POWER_REQUEST_CONTEXT
        {
            public UInt32 Version;
            public UInt32 Flags;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string SimpleReasonString;
        }
        const int POWER_REQUEST_CONTEXT_VERSION = 0;
        const int POWER_REQUEST_CONTEXT_SIMPLE_STRING = 0x1;
        const int POWER_REQUEST_CONTEXT_DETAILED_STRING = 0x2;
        static readonly Guid GUID_IDLE_RESILIENCY_SUBGROUP = new Guid(0x2e601130, 0x5351, 0x4d9d, 0x8e, 0x4, 0x25, 0x29, 0x66, 0xba, 0xd0, 0x54);
        static readonly Guid GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT = new Guid(0x3166bc41, 0x7e98, 0x4e03, 0xb3, 0x4e, 0xec, 0xf, 0x5f, 0x2b, 0x21, 0x8e);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr PowerCreateRequest(ref POWER_REQUEST_CONTEXT Context);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool PowerSetRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool PowerClearRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern bool CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr LocalFree(IntPtr hMem);
        [DllImport("PowrProf.dll", CharSet = CharSet.Unicode)]
        static extern UInt32 PowerWriteDCValueIndex(IntPtr RootPowerKey,
            [MarshalAs(UnmanagedType.LPStruct)] Guid SchemeGuid,
            [MarshalAs(UnmanagedType.LPStruct)] Guid SubGroupOfPowerSettingsGuid,
            [MarshalAs(UnmanagedType.LPStruct)] Guid PowerSettingGuid,
            int AcValueIndex);
        [DllImport("PowrProf.dll", CharSet = CharSet.Unicode)]
        static extern UInt32 PowerReadDCValueIndex(IntPtr RootPowerKey,
            [MarshalAs(UnmanagedType.LPStruct)] Guid SchemeGuid,
            [MarshalAs(UnmanagedType.LPStruct)] Guid SubGroupOfPowerSettingsGuid,
            [MarshalAs(UnmanagedType.LPStruct)] Guid PowerSettingGuid,
            out int AcValueIndex);
        [DllImport("PowrProf.dll", CharSet = CharSet.Unicode)]
        static extern UInt32 PowerGetActiveScheme(IntPtr UserPowerKey, out IntPtr ActivePolicyGuid);
    }
}

这是powercfg.exe /requests:的输出

执行:[PROCESS]\Device\HarddiskVolume4\Users\avo\Test\CsTestApp.exe禁用已连接的待机

显然,PowerRequestExecutionRequired有一个(错误(记录的超时值,因此"滥用"API不会延迟AoAc机器的实际睡眠请求。

在您的情况下,可能您最好的选择是使用PowerWriteACValueIndex来设置超时(-1禁用超时(:

    ReturnCode = PowerWriteACValueIndex(NULL,
                                   pGuidActivePowerScheme,
                                   &GUID_IDLE_RESILIENCY_SUBGROUP,
                                   &GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT,
                                   -1);

这里有一个类似的问题,没有答案。我在基于Z3700的平板电脑上尝试了PowerSetRequest/PowerRequestExecutionRequired,我看到了同样的行为,无论电源请求如何,平板电脑都会在大约5分钟内进入连接待机模式。

网上没有太多关于连接模式内部的信息,但我找到了以下可下载的文档:;连接待机介绍";。以下是有关进入连接待机模式的相关部分:

阶段名称
连接阶段。

描述
系统正在检查远程桌面连接。

执行的任务
•确定是否存在远程桌面会话
•开始跟踪未完成的电源请求。

退出时
没有连接远程桌面会话。

阶段名称
桌面活动主持人(DAM(阶段。

描述
系统暂停桌面应用程序以减少。

执行的任务
•检查未完成的电源请求(PowerRequestExecutionRequired(
•等待卓越的电源应用程序要删除的请求,或强制执行最大值电池电源超时(5分钟(。

时退出:
应用程序已清除所有未完成的电源请求或者已达到最大超时。它们在连接待机期间的消耗。

因此,对于远程连接确实有一些特殊处理,但对于其他服务,您有长达5分钟的时间来完成或暂停PowerRequestExecutionRequired活动。根据我的经验,即使平板电脑通电,这种情况也会发生。

IMO,这是一个糟糕的设计决定。它不是Windows RT平板电脑,它是功能齐全的Windows 8.1 Pro机器,如果我们想要的话,必须有一种方法让它用电池充电,至少对于值得信赖的桌面应用程序来说是这样。

可以完全禁用连接的待机模式,但在这种情况下,硬件电源按钮无法用于关闭和打开显示器。

所以,我讨厌成为一个不可能做到这一点的人,但我认为没有官方方法可以像远程访问守护程序那样禁用连接待机。我希望有人能给出更好的答案。

更新,该文档还提到了";"维护阶段":

系统执行维护任务。

•如果运行,则等待维护任务完成

(在交流电源上最常见(。

当…没有系统维护任务正在运行时退出。

•通常不到1秒。

•系统最有可能在交流电源的维护阶段发生阻塞。

也许,可以通过Windows任务调度程序API启动正在进行的维护任务,但如果使用电池电源运行,是否无论如何都不会进入CS模式尚不清楚。我还没试过这个。

这可能是最糟糕的解决方法,但如果你让groove音乐应用程序在后台播放,它仍然应该让你的电脑在其他进程中保持清醒。