How to get the thread execution state?

497 views Asked by At

On Windows, each thread has an execution state which can be set by calling SetThreadExecutionState. For example, if a thread calls:

SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED)

then Windows will not turn off the display, until that thread changes its execution state, or terminates.

It can be accessed from C#:

[DllImport("kernel32.dll", SetLastError = true)] SetThreadExecutionState(...)

But, while SetThreadExecutionState sets the execution state, there doesn't appear to be any equivalent GetThreadExecutionState API. kernel32.dll exports SetThreadExecutionState but not GetThreadExecutionState. I tried:

[DllImport("kernel32.dll", SetLastError = true)] internal static extern uint GetThreadExecutionState();

But I got a System.EntryPointNotFoundException saying GetThreadExecutionState does not exist in kernel32.dll.

How then can I find out the execution state of the current thread without changing it?

1

There are 1 answers

0
Simon Kissane On

SetThreadExecutionState does two different things:

  1. Change the current thread's execution state flags
  2. Reset system-wide idle timers

It does (1) when you pass in ES_CONTINUOUS; otherwise it does (2).

So, if you don't pass ES_CONTINUOUS as input, it will not change the current thread's execution flags, it will only reset idle timers.

When resetting idle timers, ES_SYSTEM_REQUIRED tells it to reset the system idle timer, and ES_DISPLAY_REQUIRED tells it to reset the display idle timer. If you don't pass either of ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED, it won't reset any idle timers.

In other words, SetThreadExecutionState(0) is essentially a no-op – it doesn't change the thread's execution flags, nor does it reset any idle timers. But it does do something useful – it returns the current value of the thread execution flags.

So, the GetThreadExecutionState you were looking for doesn't exist, but you don't need it, because SetThreadExecutionState(0) is equivalent.

Note that the return value always includes ES_CONTINUOUS. So, if your thread doesn't have any execution state flags set, SetThreadExecutionState(0) == ES_CONTINUOUS. Whereas, suppose the current thread has ES_SYSTEM_REQUIRED set, then SetThreadExecutionState(0) == ES_CONTINUOUS|ES_SYSTEM_REQUIRED.

If the function fails, the return value doesn't include ES_CONTINUOUS, it will return 0. As far as I am aware, the only way the function can fail, is if you pass some unsupported flag.

Note as well as the per-thread execution state flags, there are also system-wide flags. You can get those using CallNtPowerInformation with SystemExecutionState information level. If any thread has SetThreadExecutionState(ES_CONTINUOUS|ES_SYSTEM_REQUIRED), then ES_SYSTEM_REQUIRED will be set in the SystemExecutionState. In my own testing, none of the other per-thread execution state flags appear to impact the system execution state.