65.9K
CodeProject 正在变化。 阅读更多。
Home

将 PC 从待机或休眠状态唤醒

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (54投票s)

2009年12月31日

CPOL

2分钟阅读

viewsIcon

195871

downloadIcon

4698

如何在未来某个时间自动唤醒 PC。

引言

有时,在未来的某个时间自动打开 PC 可能会很有用。 为此,我们将使用一个小技巧:通常,计算机无法自行开机,但它可以从待机或休眠状态恢复(如果硬件支持)。

背景

该代码主要是两个系统计时器函数的一个包装器:CreateWaitableTimerSetWaitableTimer。 这是因为如果告诉计时器,它可以恢复系统。声明如下

[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);

我们需要提供日期和时间,但 SetWaitableTimer 要求提供一个长整数值... DateTime.ToFileTime() 是我们将用来转换托管日期/时间到系统表示的函数。 

看看程序的核心,我们在那里调用了两个 API 函数: 

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

您可以在第一行注意到一个“e.Argument”。 它在那里是因为以下代码块暂停线程的执行,直到计时器达到“唤醒时间”值

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

因此,为了避免阻塞 UI 线程,我们需要将该代码块放入一个单独的线程中,该线程由 BackgroundWorker 对象控制,我们将唤醒时间作为参数传递给该对象。 这就是“e.Argument”的来源。

我需要创建一个类以便于重用,所以我决定提供一个事件:“Woken”。 一旦后台线程退出,该事件就会被触发

public event EventHandler Woken;

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

因此,概括来说,这是完整的类: 

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

如果我们在表单上有一个 Button 和一个 DateTimePicker,我们可以这样写

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 
}

并挂起系统

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

重要提示:最后一个参数 disableWakeEvent 需要为 false

故障排除

如果软件不起作用,并不一定意味着您的硬件不支持它。 可能是某些 Windows 设置阻止了系统的唤醒。 要确保设置正确,请检查: 

  • 在“控制面板 > 电源选项 > 更改计划设置 > 更改高级电源设置 > 睡眠 > 允许唤醒计时器”中,所有项目都已启用
  • 如果您的 Windows 帐户没有设置密码,请确保在“控制面板 > 电源选项 > 更改计划设置 > 更改高级电源设置 > Brad / 其他设置 > 唤醒时需要密码”中,所有项目都已禁用(感谢 nanfang)。

关注点

我使用 BackgroundWorker 的原因很简单:Woken 事件中的代码必须与用户界面在同一线程中,以便于访问控件。 使用标准线程管理,这并非易事。

历史

  • v1.0 - 2009/12/31 - 初始版本。
  • v1.1 - 2010/10/03 - 修复了一个错误并更新了文章(故障排除部分)。
© . All rights reserved.