Sean Blakemore's Blog

Like trying to fit a square peg in a round hole

18. February 2012 13:23
by Sean
0 Comments

Power Management in C# – Stop the system or display from sleeping

18. February 2012 13:23 by Sean | 0 Comments

In a desktop application I’m working on there are a number of long running batch processes that can be run by the user. This caused problems when the user kicked off a batch process and then walked away from the computer, returning a few hours later to find that the system had gone to sleep and the batch had failed!

SetThreadExecutionState

A little searching led me to the unmanaged SetThreadExecutionState function which can be used to tell Windows that the calling thread is doing something important which requires the system to be awake. The function can be used in two ways, it can be called repeatedly to reset the system’s idle timer or it can be used in “continuous” mode where the system should not enter sleep mode until told otherwise. Check the MSDN link above for full details.

How to use it?

I took a trip to pinvoke.net and wrapped the function into a C# class. I implemented IDisposable on the class so that it can be used to scope the work being performed if required.

using (new PowerManager(ExecutionState.SystemRequired, isContinuous: true))
{
    //Do some long running work here
    DoSomeWork();
}

Alternatively, passing isContinuous as false will let you reset the system idle timer as you process work items.

using (var powerManager = new PowerManager(ExecutionState.SystemRequired, isContinuous: false))
{
    for (int i = 0; i < 1000; i++)
    {
        //Do work here
        DoSomeWork();
        //Reset the system idle timer to prevent sleep
        //before processing the next bit of work
        powerManager.ResetTimer();
    }
}

How does it work?

For your copy and pasting pleasure, here is the entire class. Please do thoroughly read the MSDN docs to understand the implications of using these different modes!

[Flags]
public enum ExecutionState : uint
{
    DisplayRequired = 0x00000002,
    SystemRequired = 0x00000001,
    AwaymodeRequired = 0x00000040
}

public class PowerManager : IDisposable
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    static extern ExecutionState SetThreadExecutionState(ExecutionState flags);

    private const uint ContinuousFlag = 0x80000000;

    public bool IsContinuous { get; private set; }
    public ExecutionState State { get; private set; }

    public PowerManager(ExecutionState state, bool isContinuous)
    {
        State = state;
        IsContinuous = isContinuous;

        if ((state & ExecutionState.AwaymodeRequired) != 0 && !isContinuous)
            throw new InvalidOperationException("Awaymode is only valid when the state is continuous");

        SetThreadExecutionState(isContinuous ? state | (ExecutionState)ContinuousFlag : state);
    }

    public void ResetTimer()
    {
        if (IsContinuous)
            throw new InvalidOperationException("The execution state is continuous");

        SetThreadExecutionState(State);
    }

    ~PowerManager()
    {
        ClearContinuousState();
    }

    public void Dispose()
    {
        ClearContinuousState();
        GC.SuppressFinalize(this);
    }

    private bool disposed;
    private void ClearContinuousState()
    {
        if (!disposed)
        {
            if (IsContinuous)
                SetThreadExecutionState((ExecutionState)ContinuousFlag);

            disposed = true;
        }
    }
}
Comments are closed