C# Suspend and Wakeup

If you need to suspend a device running on battery power programmatically and wake it back up SO THAT THE MONITOR TURNS ON you need to use the basic Suspend if the device AND the SetThreadExecutionState function. If you don’t, the device will assume there is no user activity and since no thread has signalled that it requires the monitor, the operating system will not turn it on. PLEASE NOTE: This was written for Windows XP SP2.

Please also see C# Power Management

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

public class Class1
{

private delegate void TimerSetDelegate(string WakeUpTime);
private delegate void TimerCompleteDelegate();
private event TimerSetDelegate OnTimerSet; private event TimerCompleteDelegate OnTimerCompleted;

private IntPtr handle; private uint INFINITE = 0xFFFFFFFF;

private long _SECOND = 10000000;

private void SetTimer(long Interval)
{
Int64 qwDueTime; System.Runtime.InteropServices.ComTypes.
FILETIME liDueTime;

qwDueTime = -Interval * _SECOND;

// Copy the relative time into a FILETIME struct.
liDueTime.dwLowDateTime = (Int32)(qwDueTime & 0xFFFFFFFF); liDueTime.dwHighDateTime = (Int32)(qwDueTime >> 32);

// Creating the timer delegate
TimerCompleteDelegate TimerComplete = new TimerCompleteDelegate(TimerCompleted);
TimerSetDelegate TimerSet = new TimerSetDelegate(TimerIsSet);// Creating the timer

handle = CreateWaitableTimer(IntPtr.Zero, false, "WaitableTimer"); //true

Console.WriteLine("Last Error = " + Marshal.GetLastWin32Error().ToString());

// Setting the timer
SetWaitableTimer(handle, ref liDueTime, 0, TimerComplete, IntPtr.Zero, true);
Console.WriteLine("Last Error = " + Marshal.GetLastWin32Error().ToString());

// Starting a new thread which waits for the WaitableTimer to expire
Thread t_Wait = new Thread(new ThreadStart(WaitTimer));
t_Wait.Start();

// Raising Event Timer Set
if (OnTimerSet != null)
{

OnTimerSet(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
}

}

private void WaitTimer()
{

// Waiting for the timer to expire
if (WaitForSingleObject(handle, INFINITE) != 0)
{
Console.WriteLine("Last Error = " + Marshal.GetLastWin32Error().ToString());
}
else
{
Console.WriteLine("Timer expired @ " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
}

CloseHandle(handle);

// Raising event Timer Completed
if (OnTimerCompleted != null)
{
OnTimerCompleted();
}

}

private void TimerCompleted()
{
// Routine executed once the timer has expired. This is executed independently of the
// program calling this class implementation of the OnTimerCompleted Event
}

private void TimerIsSet(string t)
{
string m = "Timer is set for " + t;
Console.WriteLine(m);
}

[DllImport("kernel32.dll")]
static extern IntPtr CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);

[DllImport("kernel32.dll")]
static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref System.Runtime.InteropServices.ComTypes.FILETIME ft,
int lPeriod, TimerCompleteDelegate pfnCompletionRoutine, IntPtr pArgToCompletionRoutine, bool fResume);

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern Int32 WaitForSingleObject(IntPtr Handle, uint Wait);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr Handle);

[DllImport("kernel32.dll")]
static extern bool CancelWaitableTimer(IntPtr hTimer);

[System.Runtime.InteropServices.DllImport("Kernel32.dll", EntryPoint = "SetThreadExecutionState",
CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private extern static EXECUTION_STATE SetThreadExecutionState(PowerThreadRequirements state);

[System.FlagsAttribute]
public enum PowerThreadRequirements : uint
{
ReleaseHold = 0x80000000,
HoldSystem = (0x00000001 | ReleaseHold),
HoldDisplay = (0x00000002 | ReleaseHold),
HoldSystemAndDisplay = (HoldSystem | HoldDisplay | ReleaseHold),
}

[System.FlagsAttribute]
public enum EXECUTION_STATE : uint //!< Add by KCL, [he] found this by searching SetThreadExecutionState on offline MSDN
{
/// Informs the system that the state being set should remain in effect until the next call
/// that uses ES_CONTINUOUS and one of the other state flags is cleared. ///
ES_CONTINUOUS = 0x80000000, ///

/// Forces the display to be on by resetting the display idle timer. ///
ES_DISPLAY_REQUIRED = 0x00000002, ///

/// Forces the system to be in the working state by resetting the system idle timer. ///
ES_SYSTEM_REQUIRED = 0x00000001,
}
}.

 
Once this is done you can have a thread that puts the device to sleep, and wakes it back up a specified number seconds from the current time. When the device wakes up, you need to let it know that you want the monitor to turn on as well.

SetTimer(SecSuspend);   

Application.SetSuspendState(System.Windows.Forms.PowerState.Suspend, false, false);   

SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED |EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED);  

//Then when you don't need the monitor anymore   
//Allow monitor to power down   
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);   

Comments are closed.