CreateWaitableTimer和SetWaitableTimer函数

用户感觉到软件的好用,就是可以定时地做一些工作,而不需要人参与进去。比如每天定时地升级病毒库,定时地下载电影,定时地更新游戏里的人物。要想实现这些功能,就可以使用定时器的API函数CreateWaitableTimer和SetWaitableTimer来实现了,这对API函数创建的时钟是比较精确的,可以达到100倍的10亿分之一秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

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

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

[DllImport("kernel32.dll")]
public static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref long pDueTime,
int lPeriod, TimerAPCProc pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine, bool fResume);

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern Int32 WaitForSingleObject(IntPtr handle, int milliseconds);
public static uint INFINITE = 0xFFFFFFFF;

// SetWaitableTimer
[DllImport("kernel32.dll")]
static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref long pDueTime,
int lPeriod, TimerCompleteDelegate pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine, bool fResume);

函数CreateWaitableTimer和SetWaitableTimer声明如下:

lpTimerAttributes是设置定时器的属性。

bManualReset是是否手动复位。

lpTimerName是定时器的名称。

hTimer是定时器的句柄。

lpDueTime是设置定时器时间间隔,当设置为正值是绝对时间;当设置为负数是相对时间。

lPeriod是周期。

pfnCompletionRoutine是设置回调函数。

lpArgToCompletionRoutine是传送给回调函数的参数。

fResume是设置系统是否自动恢复。

调用函数的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

// CreateWaitableTimer
private IntPtr handle;

private void shutdownToolStripMenuItem_Click(object sender, EventArgs e)
{
DateTime dt = DateTime.Now;
dt = dt.AddSeconds(30.0);
long duetime = dt.ToFileTime();

handle = Win32.PowerManagement.CreateWaitableTimer(IntPtr.Zero, true, "");
Win32.PowerManagement.SetWaitableTimer(handle, ref duetime, 0, null, IntPtr.Zero, true);

Thread t = new Thread(new ThreadStart(this.NewThread));
t.Start();
}

private void NewThread()
{
int ret = Win32.PowerManagement.WaitForSingleObject(handle, (int)Win32.PowerManagement.INFINITE);
MessageBox.Show("Wait object"); // ret = 0x00000000L here
}

// SetWaitableTimer
// Class that sets a WIN32 WaitableTimer. The timer will wake up the PC if the PC
// is in standby or hibernation mode.
class WaitableTimer
{
public delegate void TimerSetDelegate();
public delegate void TimerCompleteDelegate();

public event TimerSetDelegate OnTimerSet;
public event TimerCompleteDelegate OnTimerCompleted;

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

[DllImport("kernel32.dll")]
static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref long 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")]
static extern bool CancelWaitableTimer(IntPtr hTimer);

[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

private IntPtr handle;
private uint INFINITE = 0xFFFFFFFF;

public void SetTimer(long Interval)
{
// Creating the timer delegate
//
TimerCompleteDelegate TimerComplete = new TimerCompleteDelegate(TimerCompleted);
TimerSetDelegate TimerSet = new TimerSetDelegate(TimerIsSet);

// Creating the timer
//
Console.WriteLine("Creating WaitableTimer");
handle = CreateWaitableTimer(IntPtr.Zero, true, "WaitableTimer");
Console.WriteLine("Last Error = " + Marshal.GetLastWin32Error().ToString());

// Setting up the timer, the long Interval value needs to be negative if
// you want to set up a delay in milliseconds. ie
// if Interval = -60000000 the timer will expire in 1 minute. Once expired it runs the
// TimerComplete delegate
//
Console.WriteLine("Setting WaitableTimer");
SetWaitableTimer(handle, ref Interval, 0, TimerComplete, IntPtr.Zero, true);
Console.WriteLine("Last Error = " + Marshal.GetLastWin32Error().ToString()); // The error may be 1004 (Invalid flags), this is not critical
Console.WriteLine("Timer set @ " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));

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

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

// Closing the timer
//
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
//
Console.WriteLine("Timer is complete in the class");
}

private void TimerIsSet()
{
Console.WriteLine("Timer is set in the class");
}
}