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

Wake the PC from standby or hibernation

By , 4 Oct 2010
 

Introduction

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

Background

I structured the code on a base found on the internet, which is the heart of the code. Then I applied various improvements, and I made it all easily usable. Let's see how it works.

CreateWaitableTimer and SetWaitableTimer are our Windows API functions. Why? Because the Windows timer is able to resume the system, if used properly. 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 our lucky function.

This is the main code of the program:

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());
    }
}

Why is there a "e.Argument"?

long waketime = (long)e.Argument;

It's simple. This block of code pauses the execution of the thread until the timer does not reach the "wake time" value:

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

and this is not what we want. So, I inserted the block in a separate thread, controlled by a BackgrondWorker class.

I needed to create a class for easy re-use, so I decided to add an event: "Woken". Let's see how it works:

public event EventHandler Woken;

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

Very simple. Finally, this is the complete 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);

Remember: the last parameter, disableWakeEvent, must be false.

Troubleshooting

If the software doesn't work, it does not necessarily mean that your hardware doesn't support it. It's possible that some Windows settings prevent 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 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, it's not easy to do that.

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

Daniele Di Sarli
Italy Italy
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCode works, but monitor does not come ongroupPuWii21 May '13 - 21:12 
Hi, Thanks for a beautifully simple solution.
 
I am using WINXP Pro SP2. The code works error-free but...
1. The monitor stays in standby mode and I need to use mouse/keyboard to wake it up
2. The Woken date/time is correctly displayed, which indicates that the system did wake up at the correct time.
3. I tried the SetExecutionSate solution given, but after I introduced that in the code, the computer did not wake up at all and I had to do a cold boot.
 
I don't know C#, but manage to read and understand C# code.
 
I have put the entire code here (in vb.net 2005) for others to use, and give me a pointer to the error (or better still, the solution itself Smile | :) )
 
Thanks in advance
 
This is the code for the Form:
 
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Public Class frmWakeUp
    Dim wup As MyWakeUpTimer.WakeUP
 
    Private Sub timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timer1.Tick
        lblNow.Text = String.Format("Now: {0}", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString())
    End Sub
    Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click
        Dim wup As New MyWakeUpTimer.WakeUP()
        AddHandler wup.Woken, AddressOf WakeUP_Woken
        wup.SetWakeUpTime(DateTimePicker1.Value)
 
    End Sub
    Private Sub WakeUP_Woken(ByVal sender As Object, ByVal e As EventArgs)
        lblWoken.Text = String.Format("Woken: {0}", (DateTime.Now.ToShortDateString + (" " + DateTime.Now.ToLongTimeString)))
    End Sub
    Private Sub button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button2.Click
        'Application.SetSuspendState(PowerState.Hibernate, false, false)
        Application.SetSuspendState(PowerState.Suspend, False, False)
    End Sub
 
End Class
 
This is the code for the Class
 
Imports System
Imports System.Text
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles
Imports System.ComponentModel
Imports System.Threading
 
Class WakeUP
    '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

    <DllImport("kernel32.dll")> Private Shared Function CreateWaitableTimer( _
    ByVal lpTimerAttributes As IntPtr, _
    ByVal bManualReset As Boolean, _
    ByVal lpTimerName As String) As SafeWaitHandle 'IntPtr
    End Function
    'Public Declare Function CreateWaitableTimer Lib "kernel32.dll" (ByVal lpTimerAttributes As IntPtr, ByVal bManualReset As Boolean, ByVal lpTimerName As String) As SafeWaitHandle

    <DllImport("kernel32.dll")> Private Shared Function SetWaitableTimer( _
    ByVal hTimer As SafeWaitHandle, _
    ByRef pDueTime As Long, _
    ByVal lPeriod As Integer, _
    ByVal pfnCompletionRoutine As IntPtr, _
    ByVal lpArgToCompletionRoutine As IntPtr, _
    ByVal fResume As Boolean) As Boolean
    End Function
    'Public Declare Function SetWaitableTimer Lib "kernel32.dll" (ByVal hTimer As SafeWaitHandle, ByRef pDueTime As Long, ByVal lPeriod As Integer, ByVal pfnCompletionRoutine As IntPtr, ByVal lpArgToCompletionRoutine As IntPtr, ByVal fResume As Boolean) As Boolean

    Public Event Woken As EventHandler
 
    Private bgWorker As New BackgroundWorker()
 
    Public Sub New()
        AddHandler bgWorker.DoWork, AddressOf bgWorker_DoWork
        AddHandler bgWorker.RunWorkerCompleted, AddressOf bgWorker_RunWorkerCompleted
    End Sub
 
    Public Sub SetWakeUpTime(ByVal time As DateTime)
        bgWorker.RunWorkerAsync(time.ToFileTime())
    End Sub
 
    Private Sub bgWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
        RaiseEvent Woken(Me, New EventArgs())
    End Sub
 
    Private Sub bgWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
        Dim waketime As Long = CLng(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
 
End Class

GeneralMy vote of 5memberDeveshdevil8 Apr '13 - 0:39 
Great R&D..
Questionit does't work well like this.memberlsf_20085 Nov '12 - 20:15 
I updated the code like this:
//invoke wakeup
private void btnSwitch_Click(object sender, EventArgs e)
{
WakeUP wup = new WakeUP();
long delay = 30;
wup.SetWakeUpDelay(-1 * delay * 10000000);
RunCmd("rundll32 powrprof.dll,SetSuspendState");
}
//wakeup class
public void SetWakeUpDelay(long nsec)
{
bgWorker.RunWorkerAsync(nsec);
}
 
if delay >= 50, it works well;
if delay < 50, it's always waken up just in 53 ms.
Can you help me? thanks!
 
OS: win7 32b
QuestionCould you code it using vc++? thanks!memberlsf_20084 Nov '12 - 15:41 
This code is very useful for me, but i don't know C#, Could you code it using vc++? Smile | :)
Questionhow to make it wakeup daily instead of specified date?memberlordrt19 Aug '12 - 22:01 
Hello, I would like to make the app wake the computer at a specified time daily instead of using the current datetimepicker where a date needs to be specified. How can I do so? Am new to C# programming and its components by the way, still learning the way around
QuestionFuture dates won't workmemberChrisPKnight21 Jun '12 - 6:14 
On any of our DELL Desktop PCs, setting a resume date / time to, say tomorrow + 1 hour, actually resumes in 1 hour.
 
Any thoughts.
AnswerRe: Future dates won't workmemberDaniele Di Sarli21 Jun '12 - 11:19 
Hmm sorry, I can't reproduce this behavior. Can you make sure that your hardware clock time is correct?
QuestionMy vote of 5 - Now I don't have to turn the server on in the morningmemberenhzflep16 Jun '12 - 20:49 
Thanks Danielle!
 
I used your code and incorporated it into a cgi script on my server. I can now turn the server off or *more excitingly* put it to sleep overnight using my mobile phone, having it wake at a time of my choosing in the morning. [mutters slowly to self] M u s t . n o t . p l a y . f u n n y . b u g g e r s . w i t h . f r i e n d ' s . s e r v e r s.... Big Grin | :-D
QuestionDon't wake up [modified]memberchenwangfen13 Nov '11 - 15:55 
I login by user of administrator group then it done, also by standard user then PC slept but not wake up. Can you help me, pls ?

modified 13 Nov '11 - 22:30.

AnswerRe: Don't wake upmemberDaniele Di Sarli13 Nov '11 - 19:42 
What operating system are you using?

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 4 Oct 2010
Article Copyright 2009 by Daniele Di Sarli
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid