Click here to Skip to main content
Click here to Skip to main content

Wake the PC from standby or hibernation

By , 3 Feb 2014
Rate this:
Please Sign up or sign in to vote.

Introduction

Sometimes, it might be useful to automatically turn on the PC at some time in the future. To do this, we'll use a little trick: usually, a computer is not capable of powering itself up, but it's able to recover from standby or hibernation (if the hardware is capable of it).

Background

The code is mainly a wrapper around two system timer functions: CreateWaitableTimer and SetWaitableTimer. That's because the timer is able to resume the system, if told to. The declaration is:

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

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, 
                                            [In] ref long pDueTime, 
                                                      int lPeriod,
                                                   IntPtr pfnCompletionRoutine, 
                                                   IntPtr lpArgToCompletionRoutine, 
                                                     bool fResume);

We need to provide a date and a time, but SetWaitableTimer asks for a long integer value... DateTime.ToFileTime() is the function that we'll use to convert date/times from managed to system representations. 

Take a look at the core of the program, where we call the two API functions: 

long waketime = (long)e.Argument;

using (SafeWaitHandle handle = 
         CreateWaitableTimer(IntPtr.Zero, true, 
         this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
{
    if (SetWaitableTimer(handle, ref waketime, 0, 
                         IntPtr.Zero, IntPtr.Zero, true))
    {
        using (EventWaitHandle wh = new EventWaitHandle(false, 
                                             EventResetMode.AutoReset))
        {
            wh.SafeWaitHandle = handle;
            wh.WaitOne();
        }
    }
    else
    {
        throw new Win32Exception(Marshal.GetLastWin32Error());
    }
}

You can notice an "e.Argument" in the first line. It's there because the following block of code pauses the execution of the thread until the timer reaches the "wake time" value:

using (EventWaitHandle wh = new EventWaitHandle(false, 
                                EventResetMode.AutoReset))
{
    wh.SafeWaitHandle = handle;
    wh.WaitOne();
}

So, to avoid blocking the UI thread, we need to put that block of code into a separate thread, controlled by a BackgroundWorker object to which we'll pass the wake time as an argument. This is where "e.Argument" comes from.

I needed to create a class for easy re-use, so I decided to provide an event: "Woken". The event gets triggered as soon as the background thread exits:

public event EventHandler Woken;

void bgWorker_RunWorkerCompleted(object sender, 
              RunWorkerCompletedEventArgs e)
{
    if (Woken != null)
    {
        Woken(this, new EventArgs());
    }
}

So, to recap, this is the full class: 

using System;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Threading;

namespace WakeUPTimer
{
    class WakeUP
    {
        [DllImport("kernel32.dll")]
        public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, 
                                                                  bool bManualReset,
                                                                string lpTimerName);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, 
                                                    [In] ref long pDueTime, 
                                                              int lPeriod,
                                                           IntPtr pfnCompletionRoutine, 
                                                           IntPtr lpArgToCompletionRoutine, 
                                                             bool fResume);

        public event EventHandler Woken;

        private BackgroundWorker bgWorker = new BackgroundWorker();

        public WakeUP()
        {
            bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
            bgWorker.RunWorkerCompleted += 
              new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
        }

        public void SetWakeUpTime(DateTime time)
        {
            bgWorker.RunWorkerAsync(time.ToFileTime());
        }

        void bgWorker_RunWorkerCompleted(object sender, 
                      RunWorkerCompletedEventArgs e)
        {
            if (Woken != null)
            {
                Woken(this, new EventArgs());
            }
        }

        private void bgWorker_DoWork(object sender, DoWorkEventArgs e) 
        {
            long waketime = (long)e.Argument;

            using (SafeWaitHandle handle = 
                      CreateWaitableTimer(IntPtr.Zero, true, 
                      this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
            {
                if (SetWaitableTimer(handle, ref waketime, 0, 
                                       IntPtr.Zero, IntPtr.Zero, true))
                {
                    using (EventWaitHandle wh = new EventWaitHandle(false, 
                                                           EventResetMode.AutoReset))
                    {
                        wh.SafeWaitHandle = handle;
                        wh.WaitOne();
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
        }

    }
}

Using the Code

If we have a Button and a DateTimePicker on a form, we can write something like this:

private void button1_Click(object sender, EventArgs e)
{
    WakeUP wup = new WakeUP();
    wup.Woken += WakeUP_Woken;
    wup.SetWakeUpTime(dateTimePicker1.Value);
}

private void WakeUP_Woken(object sender, EventArgs e)
{
    // Do something 
}

And to suspend the system:

Application.SetSuspendState(PowerState.Suspend, false, false);

Important note: the last parameter, disableWakeEvent, needs to be false.

Troubleshooting

If the software doesn't work, it doesn't necessarily mean that your hardware doesn't support it. It's possible that some Windows setting is preventing the awakening of the system. To make sure that the settings are correct, check that: 

  • In "Control Panel > Power Options > Change Plan Settings > Change Advanced Power Settings > Sleep > Allow Wake Timers", all items are enabled.
  • If there is no password set on your Windows account, make sure that in "Control Panel > Power Options > Change Plan Settings > Change Advanced Power Settings > Brad / Additional Settings > Require a password on wakeup", all items are disabled (thanks nanfang).

Points of Interest

I used a BackgroundWorker for a simple reason: the code in the Woken event must be in the same thread of the user interface for easy access to controls. With standard thread management, that's not so trivial.

History

  • v1.0 - 2009/12/31 - Initial release.
  • v1.1 - 2010/10/03 - Fixed a bug and updated article (troubleshooting section).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

No Biography provided

Comments and Discussions

 
GeneralWorks very well with standby and hibernate [modified] Pinmembergauravkale22-Feb-11 4:00 
GeneralRe: Works very well with standby and hibernate PinmemberDaniele Di Sarli22-Feb-11 8:19 
GeneralScreen stays black Pinmemberneojudgment18-Jan-11 5:28 
GeneralRe: Screen stays black PinmemberDaniele Di Sarli18-Jan-11 5:38 
GeneralRe: Screen stays black PinmemberDaniele Di Sarli18-Jan-11 5:43 
GeneralRe: Screen stays black Pinmemberneojudgment18-Jan-11 6:03 
GeneralRe: Screen stays black PinmemberDaniele Di Sarli18-Jan-11 6:56 
GeneralRe: Screen stays black Pinmemberneojudgment27-Jan-11 10:32 
Hi Daniele,
 
it works for me if I add these few lines of code in yours :
 
C# :
  [Flags()]
  public enum EXECUTION_STATE: uint
  {
		ES_CONTINUOUS = 0X80000000L,
		ES_DISPLAY_REQUIRED = 0X2,
		ES_SYSTEM_REQUIRED = 0X1,
		ES_AWAYMODE_REQUIRED = 0X40
	}
 
	private const int VK_NONAME = 0XFC;
	private const byte KEYEVENTF_SILENT = 0X4;
 
	[DllImport("Kernel32.DLL", CharSet=CharSet.Auto, SetLastError=true)]
	protected extern static EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE state);
 
	[DllImport("user32.dll")]
	private extern static void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
.
.
.
.
.
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
		long waketime = Convert.ToInt64((int)(e.Argument));
 
		using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
		{
			if (SetWaitableTimer(handle, waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
			{
				using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
				{
					wh.SafeWaitHandle = handle;
					wh.WaitOne();
				}
			}
			else
			{
				throw new Win32Exception(Marshal.GetLastWin32Error());
			}
		}
 
		// 27/01/2011
		SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
		SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED);
 
		// keyboard event
		keybd_event(VK_NONAME, 0, KEYEVENTF_SILENT, 0);
 
	}
 
VB.NET :
  <Flags()>
 _
    Public Enum EXECUTION_STATE As UInteger
        ES_CONTINUOUS = &H80000000L
        ES_DISPLAY_REQUIRED = &H2
        ES_SYSTEM_REQUIRED = &H1
        ES_AWAYMODE_REQUIRED = &H40
    End Enum
 
    Private Const VK_NONAME As Integer = &HFC
    Private Const KEYEVENTF_SILENT As Byte = &H4
 
    <DllImport("Kernel32.DLL", CharSet:=CharSet.Auto, SetLastError:=True)>
 _
    Protected Shared Function SetThreadExecutionState(ByVal state As EXECUTION_STATE) As EXECUTION_STATE
    End Function
 
    <DllImport("user32.dll")>
 _
    Private Shared Sub keybd_event(ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As UInteger, _
                                    ByVal dwExtraInfo As Integer)
    End Sub
.
.
.
.
.
Private Sub bgWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
        Dim waketime As Long = CLng(Fix(e.Argument))
 
        Using _
            handle As SafeWaitHandle = _
                CreateWaitableTimer(IntPtr.Zero, True, Me.GetType().Assembly.GetName().Name.ToString() & "Timer")
            If SetWaitableTimer(handle, waketime, 0, IntPtr.Zero, IntPtr.Zero, True) Then
                Using wh As New EventWaitHandle(False, EventResetMode.AutoReset)
                    wh.SafeWaitHandle = handle
                    wh.WaitOne()
                End Using
            Else
                Throw New Win32Exception(Marshal.GetLastWin32Error())
            End If
        End Using
 
        ' 27/01/2011
        SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED Or EXECUTION_STATE.ES_CONTINUOUS)
        SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED)
 
        ' keyboard event
        keybd_event(VK_NONAME, 0, KEYEVENTF_SILENT, 0)
 
    End Sub
 
neojudgment
GeneralRe: Screen stays black PinmemberDaniele Di Sarli27-Jan-11 10:53 
GeneralRe: Screen stays black PinmemberSi.Jinmin26-Apr-12 20:34 
QuestionI want to write an event when the machine wakes up. How to go about it Pinmemberdigvijay_nath11-Oct-10 17:16 
AnswerRe: I want to write an event when the machine wakes up. How to go about it [modified] PinmemberKarthik. A11-Oct-10 18:48 
GeneralIt works on Windows Vista Ultimate x64 Pinmemberhswear33-Oct-10 8:40 
GeneralRe: It works on Windows Vista Ultimate x64 PinmemberDaniele Di Sarli3-Oct-10 10:06 
AnswerRe: It works on Windows Vista Ultimate x64 Pinmemberdigvijay_nath11-Oct-10 17:19 
GeneralRe: It works on Windows Vista Ultimate x64 PinmemberDaniele Di Sarli12-Oct-10 5:23 
GeneralExcellent!!! PinmemberShanthi Diana22-Aug-10 9:32 
GeneralRe: Excellent!!! PinmemberDaniele Di Sarli23-Aug-10 3:38 
GeneralIt doesn't work for me Pinmembernanfang25-Jul-10 9:45 
GeneralRe: It doesn't work for me PinmemberDaniele Di Sarli25-Jul-10 10:07 
GeneralRe: It doesn't work for me Pinmembernanfang26-Jul-10 17:47 
GeneralRe: It doesn't work for me PinmemberDaniele Di Sarli26-Jul-10 23:35 
GeneralRe: It doesn't work for me PinmemberOscar Londono3-Oct-10 3:13 
GeneralHi Pinmemberfanan_boy13-May-10 13:12 
Generalhi Pinmemberfanan_boy12-May-10 5:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 3 Feb 2014
Article Copyright 2009 by Daniele Di Sarli
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid