定时器的API函数CreateWaitableTimer和SetWaitableTimer,这对API函数创建的时钟是比较精确的,可以达到100倍的10亿分之一秒。
函数CreateWaitableTimer和SetWaitableTimer声明如下:
WINBASEAPI
__out
HANDLE
WINAPI
CreateWaitableTimerA(
WINBASEAPI
__out
HANDLE
WINAPI
CreateWaitableTimerW(
#ifdef UNICODE
#define CreateWaitableTimer
#else
#define CreateWaitableTimer
#endif // !UNICODE
WINBASEAPI
BOOL
WINAPI
SetWaitableTimer(
lpTimerAttributes是设置定时器的属性。
bManualReset是是否手动复位。
lpTimerName是定时器的名称。
hTimer是定时器的句柄。
lpDueTime是设置定时器时间间隔,当设置为正值是绝对时间;当设置为负数是相对时间。
lPeriod是周期。
pfnCompletionRoutine是设置回调函数。
lpArgToCompletionRoutine
fResume是设置系统是否自动恢复。
调用函数的例子如下:
#001
#003
#004
#005
#006
#007
#008
#009
#010
#011
#012
#013
#014
#015
#016
#017
#018
#019
#020
#021
#022
#023
#024
#025
#026
#027
#028
#029
#030
#031
#032
#033
#034
#035
#036
#037
#038
#039
#040
#041
#042
#043
#044
#045
通过异步程序调用(APC)实现的定时功能
定时器是一个在特定时间或者规则间隔被激发的内核对象。结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行。本文的例子代码显示了如何实现。
使用本定时器时,你需要把常量_WIN32_WINNT定义为0x0400,并且此常量应该在包之前定义,以确保声明合适的定时器原型函数。
通过调用CreateWaitableTimer()可以创建一个定时器,此函数返回一个指向内核对象的句柄。若定时器已经存在,你可以通过使用OpenWaitableTimer()获得一个进程相关的句柄。无论是通过CreateWaitableTimer() 还是通过OpenWaitableTimer()获得的句柄,在不需要定 时器时必须释放,方法是使用函数CloseHandle()。
定时的时间通过调用SetWaitableTimer()来设置,可以设置为一个特定的时刻(如December 16, 1999 at 9:45 PM)或者一个相对的时间(如从现在起每五分钟)。函数SetWaitableTime()定时的时间参数要求LARGE_INTEGER类型。这个值应该符合在结构体FILETIME中描述的格式。如果值是正的,代表一个特定的时刻。如果值是负的,代表以100纳秒为单位的相对时间。后面的示例代码中使用的是相对时间。在调用SetWaitableTimer()函数后,定时器将在每5秒被激发一次。
你也可以将定时器设置为周期性的自我激发,方法是向SetWaitableTimer()的第三个参数传递一个周期参数(以毫秒为单位)。在CreateWaitableTimer()的第二个参数传递FALSE可以产生一个自动归零的定时器。本例设置周期为两秒的定时器。
当设置了定时器之后,你就可以将APC与其结合起来。这里把APC函数称作完全例程。完全例程的地址作为SetWaitableTimer()的第四个参数。第五个参数是一个空类型的指针,你可以使用它来传递完全例程的参数。
在所有的APC中,要执行一个完全例程则线程必须处于监听状态。完全例程将总是被调用SetWaitableTimer()的相同的线程执行,所以此线程必须将必须其自身置于监听状态。可以调用下面的任何一个监听函数来完成监听状态的设置:
SleepEx();
WaitForSingleObjectEx();
WaitForMultipleObjectsEx();
MsgWaitForMultipleObjectsEx();
SignalObjectAndWait();
任何一个线程都有一个APC队列。在调用上面的任何一个函数时,如果线程的APC队列中有实体,则此线程不会进入休眠状态,取而代之要做的是将实体从APC队列中取出,然后调用相应的完全例程。
如果在APC队列中不存在实体,那么线程将会被挂起,直至等待条件满足为止。满足等待条件的有:一个实体加入到APC队列中,超时,激活句柄等,以及在调用MsgWaitForMultipleObjectsEx()情况下,一个消息进入到线程的一个消息队列中。若等待条件满足的是APC队列中的一个实体,那么线程会被激活,并且执行完全例程,这种情况下的函数的返回值是 WAIT_IO_COMPLETION.
【重要提示】
1、在执行完一个完全例程之后,系统会检查在APC中剩下的实体以处理。一个监视函数仅仅在处理完所有APC实体后才返回。因此,如果实体加入到APC队列的速度比处理的更快的话,则调用这些函数可能永远也不能返回。特别当定时等待的时间比起要求执行完全例程的时间更短的话,这种情况更容易发生。
2、当使用APC来实现定时器时,设置定时的线程不应该等待定时器的句柄。如果等待定时器的句柄的话,则唤起这个线程的原因是定时器被激活,而不是有实体加入到APC队列中。这时线程将不再处于监听状态,所以完全例程也不会被调用。在本例中,Sleep()被用于将线程置于监听状态。在定时器激活后,如果有实体被加入到此线程的APC队列中时,Sleep()就会唤醒此线程。
【示例代码】
01.
#define _WIN32_WINNT 0x0500
02.
#include < windows.h >
03.
#include < stdio.h >
04.
05.
#define _SECOND 10000000
06.
07.
typedef
struct
_MYDATA {
08.
TCHAR
*szText;
09.
DWORD
dwValue;
10.
} MYDATA;
11.
12.
VOID
CALLBACK TimerAPCProc(
13.
LPVOID
lpArg,
// Data value
14.
DWORD
dwTimerLowValue,
// Timer low value
15.
DWORD
dwTimerHighValue )
// Timer high value
16.
17.
{
18.
MYDATA *pMyData = (MYDATA *)lpArg;
19.
20.
printf
(
"Message: %s\nValue: %d\n\n"
, pMyData->szText,
21.
pMyData->dwValue );
22.
MessageBeep(0);
23.
24.
}
25.
26.
void
main(
void
)
27.
{
28.
HANDLE
hTimer;
29.
BOOL
bSuccess;
30.
__int64
qwDueTime;
31.
LARGE_INTEGER liDueTime;
32.
MYDATA MyData;
33.
TCHAR
szError[255];
34.
35.
MyData.szText =
"This is my data."
;
36.
MyData.dwValue = 100;
37.
38.
if
( hTimer = CreateWaitableTimer(
39.
NULL,
// Default security attributes
40.
FALSE,
// Create auto-reset timer
41.
"MyTimer"
) )
// Name of waitable timer
42.
{
43.
__try
44.
{
45.
// Create an integer that will be used to signal the timer
46.
// 5 seconds from now.
47.
qwDueTime = -5 * _SECOND;
48.
49.
// Copy the relative time into a LARGE_INTEGER.
50.
liDueTime.LowPart = (
DWORD
) ( qwDueTime & 0xFFFFFFFF );
51.
liDueTime.HighPart = (
LONG
) ( qwDueTime >> 32 );
52.
53.
bSuccess = SetWaitableTimer(
54.
hTimer,
// Handle to the timer object
55.
&liDueTime,
// When timer will become signaled
56.
2000,
// Periodic timer interval of 2 seconds
57.
TimerAPCProc,
// Completion routine
58.
&MyData,
// Argument to the completion routine
59.
FALSE );
// Do not restore a suspended system
60.
61.
if
( bSuccess )
62.
{
63.
for
( ; MyData.dwValue < 1000; MyData.dwValue += 100 )
64.
{
65.
SleepEx(
66.
INFINITE,
// Wait forever
67.
TRUE );
// Put thread in an alertable state
68.
}
69.
70.
}
71.
else
72.
{
73.
wsprintf( szError, "SetWaitableTimer failed with Error \
74.
%d.", GetLastError() );
75.
MessageBox( NULL, szError,
"Error"
, MB_ICONEXCLAMATION );
76.
}
77.
78.
}
79.
__finally
80.
{
81.
CloseHandle( hTimer );
82.
}
83.
}
84.
else
85.
{
86.
wsprintf( szError,
"CreateWaitableTimer failed with Error %d."
,
87.
GetLastError() );
88.
MessageBox( NULL, szError,
"Error"
, MB_ICONEXCLAMATION );
89.
}
90.
}