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






4.94/5 (54投票s)
如何在未来某个时间自动唤醒 PC。
引言
有时,在未来的某个时间自动打开 PC 可能会很有用。 为此,我们将使用一个小技巧:通常,计算机无法自行开机,但它可以从待机或休眠状态恢复(如果硬件支持)。
背景
该代码主要是两个系统计时器函数的一个包装器:CreateWaitableTimer
和 SetWaitableTimer
。 这是因为如果告诉计时器,它可以恢复系统。声明如下
[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 - 修复了一个错误并更新了文章(故障排除部分)。