13

I want prevent system from going to sleep/hibernate from a windows service. I am calling SetThreadExecutionState function to do that. But it seems to have no effect. I just want to know whether the function SetThreadExecutionState will for windows services. If not what will be the alternative ways to that.

Below is the C# code i am using. I am calling it on Onstart method of service.

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint SetThreadExecutionState(EXECUTION_STATE esFlags);
private void KeepAlive() 
{
     SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS)
}
Prasad
  • 1,847
  • 5
  • 21
  • 26

3 Answers3

11

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.

hebinda
  • 519
  • 6
  • 14
  • Right. Best is to start a new thread in `OnStart` and let it call `SetThreadExecutionState()`. The thread should then stay alive; an `EventWaitHandle` can be used to this end. Set it in `OnStop`. – Kay Zed Jun 16 '16 at 06:44
10

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.

Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • I am including ES_CONTINUOUS flag while calling – Prasad May 03 '11 at 13:51
  • @Prasad yes, you are. Call it every 5 minutes with that flag and it will work. – Roman Starkov May 04 '11 at 21:43
  • 1
    So if the periodical call is needed, this means that the MSDN documentation is wrong? Or is this strange 5 minutes phenomenon happening only for services? – Liviu Dec 15 '14 at 13:38
  • @Liviu I haven't tested this extensively, but I suspect it's the same for normal programs, not just services. – Roman Starkov Dec 15 '14 at 17:40
  • @romkyns Apparently this works for normal (not services) programs (C++): bool result = (NULL != SetThreadExecutionState(ES_CONTINUOUS | (allowSleep ? 0 : (ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)))) I tested on Window 7, where it runs as admin, with the sleep set at 1m, 2m, 15m ("hybrid sleep" allowed, hibernation disabled). Now it is in QA, I'll update it with they found something. – Liviu Dec 16 '14 at 14:16
  • 10
    The quote in this answer implies that _with_ `ES_CONTINUOUS`, `SetThreadExecutionState()` only need be called once. OP does this correctly. _However_, he calls it in `OnStart` which I assume runs on a short lived thread and the state expires when the thread ends. The timer solution uses a thread pool thread which is long lived, but it is a bad solution. – Kay Zed Jun 16 '16 at 06:40
  • 2
    @KayZed That is an interesting theory. You're right about `ES_CONTINUOUS`; it looks like it should work even if called just once. – Roman Starkov Jun 16 '16 at 13:57
2

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();
        }
    }
}
PatrickV
  • 2,057
  • 23
  • 30