SetThreadExecutionState is not working when called from windows service

SetThreadExecutionState is only valid for the thread that calls it. If it's called in a worker thread, even with ES_CONTINUOUS, once the worker thread is dead, the setting is not valid anymore and then screen saver will be on again.

Calling this API from a Timer will wake up a worker thread before previous thread is dead and therefore makes it work.

So if you call SetThreadExecutionState in your main thread, like UI thread in client applications, you don't need timer.


Here's my solution, hope it helps. Seems to work on Windows 10. Usage:

PowerUtilities.PreventPowerSave();

... then later

PowerUtilities.Shutdown();

Not meant to be re-callable.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace KeepAlive
{
    public static class PowerUtilities
    {
        [Flags]
        public enum EXECUTION_STATE : uint
        {
            ES_AWAYMODE_REQUIRED = 0x00000040,
            ES_CONTINUOUS = 0x80000000,
            ES_DISPLAY_REQUIRED = 0x00000002,
            ES_SYSTEM_REQUIRED = 0x00000001
            // Legacy flag, should not be used.
            // ES_USER_PRESENT = 0x00000004
        }
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern uint SetThreadExecutionState(EXECUTION_STATE esFlags);

        private static AutoResetEvent _event = new AutoResetEvent(false);

        public static void PreventPowerSave()
        {
            (new TaskFactory()).StartNew(() =>
                {
                    SetThreadExecutionState(
                        EXECUTION_STATE.ES_CONTINUOUS
                        | EXECUTION_STATE.ES_DISPLAY_REQUIRED
                        | EXECUTION_STATE.ES_SYSTEM_REQUIRED);
                    _event.WaitOne();

                },
                TaskCreationOptions.LongRunning);
        }

        public static void Shutdown()
        {
            _event.Set();
        }
    }
}

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.

(source)

You need to call this function every now and then. It's not a fire-and-forget.