SetTimer函數的原型:
UINT_PTR SetTimer(
HWND hWnd, // 窗口句柄
UINT_PTR nIDEvent, // 定時器ID,多個定時器時,可以通過該ID判斷是哪個定時器
UINT uElapse, // 時間間隔,單位為毫秒
TIMERPROC lpTimerFunc // 回調函數
);在MFC程序中SetTimer被封裝在CWnd類中,調用就不用指定窗口句柄了,例如:SetTimer(1,100,NULL); //1為ID號.100為時間例如:
// 添加兩個定時器SetTimer(1,500,NULL);SetTimer(2,1000,NULL);//停止定時器KillTimer(1);//停止ID為1的定時器KillTimer(2);//停止ID為2的定時器 定時器:void CTimerTestDlg::OnTimer(UINT nIDEvent) {switch (nIDEvent){case 1: ///處理ID為1的定時器... break;case 2: ///處理ID為2的定時器...break;}
很多應用程序都需要使用定時器,以便定期檢查狀態,並重新繪圖。學過VB的朋友知道,VB中的定時器是一個控件,我們只需要放置一個定時器控件到窗體之上,然後設置其屬性,並在程序中處理該控件產生的定時事件就行了。不過在VC中並沒有定時器控件,我們可以通過兩種方法來使用定時器:一種是直接調用WIN32函數SetTimer(),另一種則是調用CWnd::SetTimer(),實際上這兩種方法的本質是一樣的,下面我們就來看看後者的使用方法。
l 設置和釋放定時器
CWnd::SetTimer()的功能是給當前窗口設置一個定時器,該函數的原型為:
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );
由於一個窗口可以同時設置幾個定時器,SetTimer()的第一個參數nIDEvent起標識不同定時器的作用,我們可以任意取值,只要不與其它定時器的標識重復就行了,以便將來能正確分辨出是哪個定時器發出的定時消息。nElapse表示每隔多少毫秒產生一次定時消息。lpfnTimer是一個函數指針,如果傳遞一個NULL指針,那麼在產生定時中斷時,Windows會把一條WM_TIMER消息放入到程序的消息隊列之中,最終由CWnd對象相應的處理函數來處理該消息;如果傳遞了一個真正的函數指針,那麼在產生定時中斷時,Windows將直接調用該函數,由該函數來處理定時消息。這也就是說,我們可以使用兩種方式來處理定時消息,它們有什麼區別呢?
首先,WM_TIMER是一條低優先級的消息。如果消息隊列之中還有其它高優先級的消息,那麼WM_TIMER將被堵塞,直到最後才會被處理。其次,WM_TIMER被程序從消息隊列中取回之後,還要經過一系列的過程才能被傳遞到它的處理函數。把這兩點結合起來可以看出,在第一種方式下,定時消息有可能要拖延一段時間才會被處理,而第二種方式則不同,由於Windows將直接調用指定的函數,所以延遲時間要短得多。
由於Schedule對實時性要求並不苛刻,只要能精確到分鐘左右就可以了,另外Schedule內部要進行的運算和處理也不多,所以心鈴決定選用第一種方式,准備在視類中處理定時消息。在第十二講給出的CScheduleView::OnInitialUpdate()的最後一行語句便是設置定時器的代碼,現在我們可以把它前面的注釋符號刪掉了,這個定時器將使Schedule在每過20秒左右的時間得到一次重新檢查各事件狀態的機會。
定時器是一種有限的系統資源,如果多個程序都需要使用定時器,那麼有可能會出現設置不成功的情況,因此我們應在調用SetTimer()函數時檢查它的返回值,如果返回值是0,就表示系統中已經沒有可用的定時器了。Schedule沒有進行這步檢查,心鈴想請大家自己添上,並決定在出現這種情況時應該怎麼辦。
SetTimer()還有一個作用是它能夠重新設置已分配的定時器的定時間隔,只要在nIDEvent中傳遞已分配的定時器的標識,那麼該定時器的定時間隔就會改變成nElapse中指定的新值。
在程序退出時,我們應該將已分配的定時器釋放掉。為此我們利用ClassWizard為視類添加一個處理WM_DESTROY消息的函數OnDestroy(),並編寫以下代碼:
void CScheduleView::OnDestroy() {
KillTimer(1);
CListCtrl & lst=(this->GetListCtrl());
CImageList *pImageList=lst.GetImageList(LVSIL_SMALL);
delete pImageList;
CListView::OnDestroy();
}
KillTimer()用於釋放定時器,其參數應等於SetTimer()的nIDEvent。如果程序分配了多個定時器,那麼應調用多次KillTimer()。OnDestroy()同時也把與CListView內部的List控件關聯的圖象列表釋放了。
l 編寫消息處理函數
使用定時器的第一種方式需要用到一個回調函數,該函數必須具有以下原型:
void CALLBACK EXPORT TimerProc( HWND hWnd, UINT nMsg, UINT nIDEvent,
DWORD dwTime );
由於我們不使用這種方式,所以TimerProc各參數的含義請大家自己查閱MSDN庫,下面我們來看看如何為第二種方式編寫消息處理函數。
首先我們利用ClassWizard為視類添加一個處理WM_TIMER消息的函數OnTimer()。它只有一個參數nIDEvent,檢查這個值便可以知道WM_TIMER消息是由哪個定時器發出的。下面便是OnTimer()的實現代碼:
void CScheduleView::OnTimer(UINT nIDEvent) {
CTime tiNow=CTime::GetCurrentTime();
CTimeSpan tdif(0,0,15,0);
CScheduleDoc * pDoc=GetDocument();
CString Message;
struct ScheduleItem* pUp,*pDown;
pUp=pDoc->m_pNearestTaskUp;
pDown=pDoc->m_pNearestTaskDown;
if(nIDEvent==1) {
if( ( (pUp!=NULL) && (pUp->ti<tiNow-tdif) ) ||
( (pDown!=NULL) && (pDown->ti<=tiNow+tdif) ) ) {
pDoc->SearchNearestTask();
//更新事件條目的狀態
pDoc->UpdateState();
pDoc->UpdateAllViews(NULL,0,NULL);
//更新顯示
if((pDown!=NULL) && ( pDown->ti <= (tiNow+tdif) )) {
AfxGetMainWnd()->ShowWindow(SW_NORMAL);
SetForegroundWindow(); //把窗口顯示在前台
Message="請注意:\""; //准備提示信息
Message += pDown->des;
Message += "\" 的時間很快就要到了!";