当计数器触发的时候,Microsoft允许计时器把一个异步过程调用(APC)放到SetWaitableTimer的调用线程的队列中。
通常,当我们调用SetWaitableTimer的时候,会给pfnCompletionRoutine 和 pvArgToCompletionRoutine 两个参数传NULL。当SetWaitableTimer看到这两个参数为NULL的时候,它知道时间一到应该触发计时器对象。但是如果希望时间一到就让计时器把一个ARC添加到队列中去,就必须实现一个计时器APC函数,并把函数的地址传入。
代码如下:
// 计时器内核对象.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <process.h>
#include <iostream>
using namespace std;
HANDLE hTimer1;
DWORD WINAPI ThreadFun(PVOID pvParam);
//APC函数,用于显示当前的系统时间
VOID WINAPI TimerAPCRoutine(PVOID pvArgToCompletionRoutine,
DWORD dwTimerLowValue,DWORD dwTimerHighValue)
{
FILETIME ftUTC,ftLocal;
SYSTEMTIME st;
TCHAR szBuf[256];
ftUTC.dwHighDateTime = dwTimerHighValue;
ftUTC.dwLowDateTime = dwTimerLowValue;
FileTimeToLocalFileTime(&ftUTC,&ftLocal);
FileTimeToSystemTime(&ftLocal,&st);
GetDateFormat(LOCALE_USER_DEFAULT,DATE_LONGDATE,&st,NULL,szBuf,_countof(szBuf));
_tcscat_s(szBuf,_countof(szBuf),TEXT(" "));
GetTimeFormat(LOCALE_USER_DEFAULT,0,&st,NULL,_tcschr(szBuf,TEXT('\0')),(int)(_countof(szBuf)-_tcslen(szBuf)));
MessageBox(NULL,szBuf,TEXT("Timer went of at ..."),MB_OK);
}
int _tmain(int argc, _TCHAR* argv[])
{
SYSTEMTIME st;
FILETIME ftLocal,ftUTC;
LARGE_INTEGER liUTC;
hTimer1 = CreateWaitableTimer(NULL,FALSE,NULL);
st.wYear = 2011;
st.wMonth = 7;
st.wDayOfWeek = 0;
st.wDay = 6;
st.wHour = 17;
st.wMinute = 16;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st,&ftLocal); //系统时间转换文件时间
LocalFileTimeToFileTime(&ftLocal,&ftUTC); //转换为 全球标准时间
liUTC.HighPart = ftUTC.dwHighDateTime;
liUTC.LowPart = ftUTC.dwLowDateTime;
SetWaitableTimer(hTimer1,&liUTC,6*1000,NULL,NULL,FALSE); //设置一个等待的计数器1
HANDLE hTimer2 = CreateWaitableTimer(NULL,TRUE,NULL);
HANDLE hThread1 = CreateThread(NULL,0,ThreadFun,hTimer1,0,0);
system("pause");
return 0;
}
DWORD WINAPI ThreadFun(PVOID pvParam)
{
LARGE_INTEGER li = {0};
SetWaitableTimer(pvParam,&li,5000,TimerAPCRoutine,NULL,FALSE);//当线程处于 可提醒状态,APC函数被调用
WaitForSingleObjectEx(hTimer1, INFINITE,TRUE);//因为这里等待 计数器1 被触发,所以线程出去 可提醒状态
return 0;
}
计数器被触发的时候,当且仅当SetWaitableTimer()的调用线程正处于 可提醒状态,这个函数会被同一个线程调用。换句话说,线程必须是由于调用 SleepEX,WaitForSingleObjectEx,WaitForMultipleObjectsEX,MSGWaitForMultipleObjectsEx 或 SignalObjectAndWait而进入等待的状态。否则,系统不会把计时器的APC函数添加到队列中。
总结:
(1)只有当所有的APC函数都处理完毕后,才会返回 可警告函数。因此,我们必须确保自己的TimerAPCRoutine函数会在计数器再次被触发之前结束,不然APC函数加入队列的少速度就快过了处理他们的速度。
(2)线程不应该在等待一个计时器句柄的同时以可提醒的方式等待以一个计时器。