Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I need to disable connected standby mode until my Desktop application has finished. The desired behavior should be similar to what happens when I connect to that machine via Remote Desktop. That is, the screen is switched off, but the system doesn't go into sleep until I disconnect.

Is there any documented or undocumented way to get the same behavior for my application?

I tried PowerSetRequest with PowerRequestExecutionRequired and/or PowerRequestAwayModeRequired, but the system still goes into connected standby mode in a few mins. I currently use PowerRequestDisplayRequired to keep it alive, but the screen always stays on.

EDITED. This is the test application. The timer is ticking for no more than 5 minutes after I press the hardware power button and the screen turns off (running on battery).

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);
    }
}

This is the output from powercfg.exe /requests:

EXECUTION:
[PROCESS] \Device\HarddiskVolume4\Users\avo\Test\CsTestApp.exe
Disable Connected Standby
like image 702
avo Avatar asked Apr 30 '14 22:04

avo


2 Answers

Apparently there's a (poorly) documented timeout value associated with PowerRequestExecutionRequired so that "misusing" the API would not delay an actual sleep request for AoAc machines.

In your case, probably your best bet is to use PowerWriteACValueIndex to set the timeout (-1 disables the timeout):

    ReturnCode = PowerWriteACValueIndex(NULL,
                                   pGuidActivePowerScheme,
                                   &GUID_IDLE_RESILIENCY_SUBGROUP,
                                   &GUID_EXECUTION_REQUIRED_REQUEST_TIMEOUT,
                                   -1);
like image 84
Eric Brown Avatar answered Oct 31 '22 17:10

Eric Brown


Here is a similar question, unanswered. I tried PowerSetRequest / PowerRequestExecutionRequired on my Z3700-based tablet, and I'm seeing the same behavior, the tablet goes into connected standby mode in ~5 mins regardless of this power request.

There's not so much information available on the web about connected mode internals, but I've found the following downloadable document: "Introduction to Connected Standby". Here is the relevant part about entering connected standby mode:

Phase name:
Connection Phase.

Description:
The system is checking for remote desktop connections.

Tasks performed:
• Determine if remote desktop session(s) exist.
• Begin tracking outstanding Power Requests.

Exit when:
There are no remote desktop sessions connected.

...

Phase name:
Desktop Activity Moderator (DAM) Phase.

Description:
The system pauses desktop applications to reduce.

Tasks performed:
• Check for outstanding Power Requests (PowerRequestExecutionRequired).
• Wait for outstanding Power Requests to be dropped by the application, or enforce a maximum time-out on battery power (5 minutes).

Exited when:
All outstanding Power Requests have been cleared by applications or the maximum time-out has been reached. their consumption during Connected Standby.

So, there's indeed some special treatment for remote connections, but for other services you have as many as 5 minutes to finish or pause your PowerRequestExecutionRequired activity. In my experience, this is happening even when the tablet is powered.

IMO, that's a bad design decision. It is not a Windows RT tablet, it's full-featured Windows 8.1 Pro machine and there has to be a way to keep it alive on batteries if we want so, at least for trusted desktop apps.

It's possible to disable connected standby mode altogether, but in such case the hardware power button cannot be used to turn the monitor off and on.

So, I hate to be a this-is-not-possible guy, but I think there is no official method to disable connected standby the way the remote access daemon does it. I hope someone will post a better answer.

Updated, this document also mentioned the "Maintenance Phase":

The system executes Maintenance Tasks.

• Wait for maintenance tasks to complete if running

(most common on AC power).

Exited when… No system maintenance tasks are running.

• Typically, less than 1 second.

• The system is most likely to block on maintenance phase on AC power.

Perhaps, it's possible to start an ongoing maintenance task via Windows Task Scheduler API, although it's not clear if CS mode won't be entered anyway if running on battery power. I haven't tried this.

like image 20
noseratio Avatar answered Oct 31 '22 18:10

noseratio