定時器是在特定時間或固定間隔被觸發的核心物件。結合非同步程序呼叫 (APC),可讓回呼函數在定時器觸發時執行。本文展示完整實作範例。
使用前需定義常數 _WIN32_WINNT 為 0x0400,且必須在包含標頭檔前定義,確保宣告正確的定時器原型函數。
CreateWaitableTimer() 回傳核心物件控制代碼。若已存在,可用 OpenWaitableTimer() 取得進程相關控制代碼。無論何種方式取得,不需時必須用 CloseHandle() 釋放。SetWaitableTimer() 可設定絕對時間(如 1999/12/16 21:45)或相對時間(如每 5 分鐘)。參數需 LARGE_INTEGER 格式,符合 FILETIME 結構。正值表絕對時間,負值表以 100 奈秒為單位的相對時間。範例使用相對時間,每 5 秒觸發一次。SetWaitableTimer() 第三參數傳遞週期 (毫秒)。CreateWaitableTimer() 第二參數傳 FALSE 建立自動重置定時器。範例設定 2 秒週期。SetWaitableTimer() 第四參數。第五參數為 void 指標,可傳遞參數。所有 APC 中,要執行完成常式,執行緒必須處於警覺狀態。完成常式總在呼叫 SetWaitableTimer() 的同一執行緒執行,故該執行緒必須自我置於警覺狀態。可呼叫以下任一函數:
SleepEx()WaitForSingleObjectEx()WaitForMultipleObjectsEx()MsgWaitForMultipleObjectsEx()SignalObjectAndWait()每個執行緒擁有 APC 佇列。呼叫上述函數時,若佇列有實體,執行緒不進入睡眠,改為取出實體並呼叫對應完成常式。若無實體,執行緒掛起直到等待條件滿足:APC 佇列新增實體、逾時、觸發控制代碼、或 MsgWaitForMultipleObjectsEx() 情況下訊息佇列有新訊息。若因 APC 實體喚醒,函數回傳 WAIT_IO_COMPLETION。
Sleep() 置於警覺狀態,定時器觸發後 APC 實體加入佇列即喚醒執行緒。#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>
#define _SECOND 10000000
typedef struct _MYDATA {
TCHAR *szText;
DWORD dwValue;
} MYDATA;
VOID CALLBACK TimerAPCProc(
LPVOID lpArg, // 資料值
DWORD dwTimerLowValue, // 定時器低位值
DWORD dwTimerHighValue // 定時器高位值
) {
MYDATA *pMyData = (MYDATA *)lpArg;
printf( "Message: %s\nValue: %d\n\n", pMyData->szText, pMyData->dwValue );
MessageBeep(0);
}
void main( void ) {
HANDLE hTimer;
BOOL bSuccess;
__int64 qwDueTime;
LARGE_INTEGER liDueTime;
MYDATA MyData;
TCHAR szError[255];
MyData.szText = "This is my data.";
MyData.dwValue = 100;
if ( hTimer = CreateWaitableTimer(
NULL, // 預設安全屬性
FALSE, // 建立自動重置定時器
"MyTimer" ) ) // 可等待定時器名稱
{
__try {
// 建立整數,從現在起 5 秒後觸發
qwDueTime = -5 * _SECOND;
// 複製相對時間到 LARGE_INTEGER
liDueTime.LowPart = (DWORD)( qwDueTime & 0xFFFFFFFF );
liDueTime.HighPart = (LONG)( qwDueTime >> 32 );
bSuccess = SetWaitableTimer(
hTimer, // 定時器物件控制代碼
&liDueTime, // 觸發時間
2000, // 週期 2 秒
TimerAPCProc, // 完成常式
&MyData, // 完成常式參數
FALSE ); // 不恢復暫停系統
if ( bSuccess ) {
for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 ) {
SleepEx( INFINITE, TRUE ); // 無限期等待,進入警覺狀態
}
} else {
wsprintf( szError, "SetWaitableTimer failed with Error %d.", GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}
}
__finally {
CloseHandle( hTimer );
}
} else {
wsprintf( szError, "CreateWaitableTimer failed with Error %d.", GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}
}
原文參考:Using a Waitable Timer with an Asynchronous Procedure Call