SetThreadExecutionState(ES_SYSTEM_REQUIRED) does not prevent system sleep on Windows 11
Asked Answered
C

2

6

In my file transfer application (WinSCP), I use SetThreadExecutionState(ES_SYSTEM_REQUIRED) to prevent the system from going into sleep mode while a file transfer is in progress. But this does not work anymore on Windows 11.

I didn't find any reference about different requirements for an application to prevent sleep mode on Windows 11.

My application is a C++ Win32 app. But I can reproduce the same problem with a trivial .NET 5 WinForms C# application.

private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label label1;
public enum EXECUTION_STATE : uint
{
    ES_SYSTEM_REQUIRED = 0x00000001
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint SetThreadExecutionState(EXECUTION_STATE esFlags);

// The timer ticks every second.
private void timer1_Tick(object sender, EventArgs e)
{
    label1.Text = (int.Parse(label1.Text) + 1).ToString();
    SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED);
}

As long as the application is running on Windows 10, the system never goes into sleep mode. But Windows 11 goes to sleep as scheduled.

Windows File Explorer on Windows 11 successfully prevents the sleep while it is transferring files. So it's not like it's not possible to prevent Windows 11 from going into sleep mode.

Why doesn't SetThreadExecutionState(ES_SYSTEM_REQUIRED) work anymore on Windows 11? Is there a different API on Windows 11 for this task?

Chaille answered 30/5, 2022 at 15:17 Comment(0)
C
5

It seems that the SetThreadExecutionState(ES_SYSTEM_REQUIRED) "reset the idle timer" API (i.e. without ES_CONTINUOUS flag) is broken in Windows 11.

The ES_CONTINUOUS API works. So I had to redesign my application to use that one. As my application can have multiple threads, each doing lengthy operation, repeatedly calling the SetThreadExecutionState(ES_SYSTEM_REQUIRED) from each of these threads during the operations was more convenient.

Now I have to have a separate thread whose sole purpose is to report the application state to the system. First time the SetThreadExecutionState(ES_SYSTEM_REQUIRED) would be previously called by any thread now triggers the "state thread" to call SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS). Further calls just reset an internal 5 seconds timer. If that timer eventually expires, the state thread calls SetThreadExecutionState(ES_CONTINUOUS).

Chaille answered 6/6, 2022 at 9:47 Comment(2)
FYI. One from Microsoft, 'This is by design. ES_CONTINUOUS is implemented as power requests. Prior to Windows 11 power requests were held an additional 2 minutes after they were dropped by the application. This caused issue as some applications would periodically take a power request for a short period keep systems out of sleep indefinitely. This behavior was removed in Windows 11 and now systems are eligible to sleep as soon as the last power request is dropped.'Aristarchus
@Aristarchus But my question is about change in ES_SYSTEM_REQUIRED behavior, not ES_CONTINUOUS.Chaille
A
1

According to SetThreadExecutionState,

Calling SetThreadExecutionState without ES_CONTINUOUS simply resets the idle timer; to keep the display or system in the working state, the thread must call SetThreadExecutionState periodically.

Alternatively, Calling SetThreadExecutionState as the document example shows:

// Enable away mode and prevent the sleep idle time-out.
//
SetThreadExecutionState(ES_CONTINUOUS | ES_AWAYMODE_REQUIRED);

//
// ...
//

//
// Clear EXECUTION_STATE flags to disable away mode and allow the system to idle to sleep normally.
//
SetThreadExecutionState(ES_CONTINUOUS);
Aristarchus answered 31/5, 2022 at 1:15 Comment(7)
Thanks. Though I do "call SetThreadExecutionState periodically". + If I understand the documentation correctly, you keep the system up by either repeatedly calling SetThreadExecutionState without ES_CONTINUOUS; or by calling it once with ES_CONTINUOUS. So while your code with ES_CONTINUOUS should work too (and it indeed seem to work), it does not explain why my code does not. Or do you have any reason to believe the API without ES_CONTINUOUS is not valid anymore on Windows 11?Chaille
As far as I'm concerned, ES_SYSTEM_REQUIRED Forces the system to be in the working state by resetting the system idle timer once.Aristarchus
Yes, that's why I'm calling it on timer every second in the example code. And that works as expected on Windows 10.Chaille
@MartinPrikryl I reproduced and I'm looking into the behavior.Aristarchus
"Away mode should be used only by media-recording and media-distribution applications"Zoophilous
Just to add some info I found out after trying the above in PowerShell, for multiple input arguments, the pipes aren't passed correctly by PowerShell. You have to use -bor in their place to represent bitwise or operators, e.g., ( $ES_CONTINUOUS -bor $ES_SYSTEM_REQUIRED ), where the variables are the hexadecimal representations from the MSDocs.Kashgar
Do you have any updates on this @YangXiaoPo-MSFT?Kwarteng

© 2022 - 2024 — McMap. All rights reserved.