使用者感覺到軟體的好用,就是可以定時地做一些工作,而不需要人蔘與進去。比如每天定時地升級病毒庫,定時地下載電影,定時地更新遊戲裡的人物。要想
實現這些功能,就可以使用定時器的API函數CreateWaitableTimer和SetWaitableTimer來實現了,這對API函數建立的
時鐘是比較精確的,可以達到100倍的10億分之一秒。
函數CreateWaitableTimer和SetWaitableTimer聲明如下:
WINBASEAPI
__out
HANDLE
WINAPI
CreateWaitableTimerA(
__in_opt LPSECURITY_ATTRIBUTES lpTimerAttributes,
__in BOOL bManualReset,
__in_opt LPCSTR lpTimerName
);
WINBASEAPI
__out
HANDLE
WINAPI
CreateWaitableTimerW(
__in_opt LPSECURITY_ATTRIBUTES lpTimerAttributes,
__in BOOL bManualReset,
__in_opt LPCWSTR lpTimerName
);
#ifdef UNICODE
#define CreateWaitableTimer CreateWaitableTimerW
#else
#define CreateWaitableTimer CreateWaitableTimerA
#endif // !UNICODE
WINBASEAPI
BOOL
WINAPI
SetWaitableTimer(
__in HANDLE hTimer,
__in const LARGE_INTEGER *lpDueTime,
__in LONG lPeriod,
__in_opt PTIMERAPCROUTINE pfnCompletionRoutine,
__in_opt LPVOID lpArgToCompletionRoutine,
__in BOOL fResume
);
lpTimerAttributes是設定定時器的屬性。
bManualReset是是否手動複位。
lpTimerName是定時器的名稱。
hTimer是定時器的控制代碼。
lpDueTime是設定定時器時間間隔,當設定為正值是絕對時間;當設定為負數是相對時間。
lPeriod是周期。
pfnCompletionRoutine是設定回呼函數。
lpArgToCompletionRoutine是傳送給回呼函數的參數。
fResume是設定系統是否自動回復。
調用函數的例子如下:
#001 //建立定時器
#002 //蔡軍生 2007/11/06 QQ:9073204 深圳
#003 int CreateTestTimer(void)
#004 {
#005 HANDLE hTimer = NULL;
#006 LARGE_INTEGER liDueTime;
#007
#008 //設定相對時間為10秒。
#009 liDueTime.QuadPart = -100000000;
#010
#011 ; //建立定時器。
#012 hTimer = CreateWaitableTimer(NULL, TRUE, _T("TestWaitableTimer"));
#013 if (!hTimer)
#014 {
#015 return 1;
#016 }
#017
#018 OutputDebugString(_T("10秒定時器/r/n"));
#019
#020 // 設定10秒鐘。
#021 if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))
#022 {
#023 //
#024 CloseHandle(hTimer);
#025 return 2;
#026 }
#027
#028 //等定時器有訊號。
while(true)
{
#029 if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)
#030 {
#031 OutputDebugString(_T("10秒定時器出錯了/r/n"));
#032 //
#033 CloseHandle(hTimer);
#034 return 3;
#035 }
#036 else
#037 {
#038 //10秒鐘到達。
SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0);//將hTimer資訊重設為無訊號,如果不然就會不斷的輸出
#039 OutputDebugString(_T("10秒定時器到了/r/n")); //“10秒定時器到了”這一句
#040 }
#041 }
#042 //
#043 CloseHandle(hTimer);
#044 return 0;
#045 }
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/jiangxinyu/archive/2008/07/23/2696447.aspx
通過非同步程式調用(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()就會喚醒此線程。
【範例程式碼】
#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, // Data value
DWORD dwTimerLowValue, // Timer low value
DWORD dwTimerHighValue ) // Timer high value
{
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, // Default security attributes
FALSE, // Create auto-reset timer
"MyTimer" ) ) // Name of waitable timer
{
__try
{
// Create an integer that will be used to signal the timer
// 5 seconds from now.
qwDueTime = -5 * _SECOND;
// Copy the relative time into a LARGE_INTEGER.
liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF );
liDueTime.HighPart = (LONG) ( qwDueTime >> 32 );
bSuccess = SetWaitableTimer(
hTimer, // Handle to the timer object
&liDueTime, // When timer will become signaled
2000, // Periodic timer interval of 2 seconds
TimerAPCProc, // Completion routine
&MyData, // Argument to the completion routine
FALSE ); // Do not restore a suspended system
if ( bSuccess )
{
for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 )
{
SleepEx(
INFINITE, // Wait forever
TRUE ); // Put thread in an alertable state
}
}
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 );
}
}
摘自:http://www.vckbase.com/document/viewdoc/?id=1587