Windows定時器是一種周期性的消息產(chǎn)生裝置,它會每隔一段指定時間發(fā)送一次定時消息WM_TIMER。它是一個很重要的系統(tǒng)消息,當(dāng)系統(tǒng)所設(shè)置的時間到達(dá)以后,系統(tǒng)就會自動發(fā)送該消息。與該消息聯(lián)系密切的函數(shù)是SetTimer(),它設(shè)置一個系統(tǒng)時鐘,當(dāng)設(shè)置的時間到時,系統(tǒng)產(chǎn)生WM_TIMER消息。通過對SetTimer()函數(shù)的參數(shù)進(jìn)行設(shè)置,可以告訴用戶哪一個時鐘的時間到了。因此,可以將一些周期性的工作放入WM_TIMER的消息處理函數(shù)中。 定時器的使用一般遵循下列步驟: (1)使用SetTimer()函數(shù)設(shè)置定時器 SetTimer()函數(shù)的原型如下:
UINT SetTimer(UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT*lpfnTimer)(HWND,UINT, UINT, DWORD));
其中,參數(shù)nIDEvent為新創(chuàng)建的定時器標(biāo)識號碼(非零),當(dāng)一個應(yīng)用程序需要多個定時器時,靠此參數(shù)的不同來加以區(qū)別;參數(shù)nElapse為定時器間隔,以毫秒為單位,當(dāng)由該參數(shù)規(guī)定的時間到后,系統(tǒng)發(fā)送WM_TIMER消息;參數(shù)lpfnTimer為指定處理消息WM_TIMER的函數(shù),通常為NULL時,表示由CWnd對象的OnTimer成員函數(shù)來處理該消息,當(dāng)然也可以超載該函數(shù)。 如果創(chuàng)建成功,則返回新的定時器的號碼:否則返回0。 (2)超載OnTimer()函數(shù),完成用戶希望的操作 通過第一步設(shè)置的定時器會按其設(shè)置的時間間隔向應(yīng)用程序發(fā)送WM_TIMER消息,為了接收和處理該消息,應(yīng)超載消息處理函數(shù)OnTimer()(可以由ClassWizard自動產(chǎn)生),其函數(shù)原型如下:
afx_msg void OnTimer(UINT nIDEvent);
其中,參數(shù)nIDEvent為定時器的標(biāo)識。若在程序中設(shè)置了多個定時器時,靠此參數(shù)的不同來加以區(qū)別。 (3)撤銷定時器 CWnd::KillTimer
定時器使用完后,可以通過調(diào)用KillTimer()函數(shù)來清除定時器,其函數(shù)原型如下:
BOOL KillTimer(int nIDEvent);
其中,參數(shù)nIDEvent為準(zhǔn)備清除的定時器號碼,該參數(shù)的值必須在SetTimer()函數(shù)中設(shè)置過,即不能夠清除一個不存在的定時器號碼。 【例9.4】 本程序在例9.3程序的基礎(chǔ)上增加定時器的功能。當(dāng)按B鍵時,啟動定時器,屏幕上的圖形自動移動;當(dāng)按S鍵時,撤銷定時器,停止自動移動。 程序演示 (1)在字符消息映射函數(shù)中添加啟動和撤銷定時器的代碼
void CKeyMsgView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { … switch(nChar) { … case 'b': case 'B': SetTimer(1,100,NULL); //建立一號定時器,時間間隔為100ms break; case 's': case 'S': KillTimer(1); //撤銷一號定時器 break; } … }
(2)添加WM_TIMER的消息映射函數(shù)OnTimer()的實現(xiàn)代碼
void CKeyMsgView::OnTimer(UINT nIDEvent) { //圖形往右下角自動移動 CRect OldRect=m_Rect; m_Rect.OffsetRect(CPoint(1,1)); //圖形往右下角移動一個像素 OldRect.UnionRect(OldRect,m_Rect); InvalidateRect(OldRect); //將指定矩形區(qū)域OldRect的內(nèi)容刷新 CView::OnTimer(nIDEvent); }
該TimerProc函數(shù)是一個應(yīng)用程序定義的回調(diào)函數(shù),用于處理WM_TIMER消息。
函數(shù)原型: VOID CALLBACK TimerProc( HWND hwnd, // 定時器消息的窗口句柄 UINT uMsg, // WM_TIMER 消息 UINT idEvent, // 定時器標(biāo)識符 DWORD dwTime // 當(dāng)前系統(tǒng)時間 );
參數(shù)說明: hwnd 標(biāo)識了與定時器進(jìn)行關(guān)聯(lián)的窗口。 uMsg 指定WM_TIMER消息。 idEvent 指定定時器的標(biāo)識符。 dwTime 指定自窗口啟動開始所經(jīng)過的毫秒數(shù)。這是GetTickCount函數(shù)的返回值。 返回值: 這個函數(shù)沒有返回值。 備注: TimerProc是一個應(yīng)用程序定義函數(shù)名稱的占位符。
百度知道: SetTimer函數(shù)的用法 1 )用WM_TIMER來設(shè)置定時器 先請看SetTimer這個API函數(shù)的原型 UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent, // 定時器ID,多個定時器時,可以通過該ID判斷是哪個定時器 UINT uElapse, // 時間間隔,單位為毫秒 TIMERPROC lpTimerFunc // 回調(diào)函數(shù) ); 例如 SetTimer(m_hWnd,1,1000,NULL); //一個1秒觸發(fā)一次的定時器 在MFC程序中SetTimer被封裝在CWnd類中,調(diào)用就不用指定窗口句柄了 于是SetTimer函數(shù)的原型變?yōu)椋?/font> UINT SetTimer(UINT nIDEvent,UINT nElapse,void(CALLBACK EXPORT *lpfnTimer)(HWND,UINT ,UINT ,DWORD)) 當(dāng)使用SetTimer函數(shù)的時候,就會生成一個計時器。函數(shù)中nIDEvent指的是計時器的標(biāo)識,也就是名字。nElapse指的是時間間隔, 也就是每隔多長時間觸發(fā)一次事件。第三個參數(shù)是一個回調(diào)函數(shù),在這個函數(shù)里,放入你想要做的事情的代碼,你可以將它設(shè)定為NULL, 也就是使用系統(tǒng)默認(rèn)的回調(diào)函數(shù),系統(tǒng)默認(rèn)認(rèn)的是onTime函數(shù)。這個函數(shù)怎么生成的呢?你需要在需要計時器的類的生成onTime函數(shù): 在ClassWizard里,選擇需要計時器的類,添加WM_TIME消息映射,就自動生成onTime函數(shù)了。然后在函數(shù)里添加代碼,讓代碼實現(xiàn)功能。 每隔一段時間就會自動執(zhí)行一次。 SetTimer計時器是系統(tǒng)資源,使用完畢應(yīng)及時用KillTimer銷毀,關(guān)于SetTimer的返回值:如果hWnd為NULL,返回值為新建立的timer的ID,如果hWnd非NULL,返回一個非0整數(shù),如果SetTimer調(diào)用失敗則返回0 ,簡言之,SetTimer的返回值用于將來的銷毀。 改變計時器的時間間隔 如果想將一個已經(jīng)存在的計時器設(shè)定為不同的時間間隔,可以簡單地用不同的時間值再次調(diào)用SetTimer。 計時器精確嗎? 計時器并不精確。有兩個原因: 原因一:Windows計時器是硬件和ROM BIOS架構(gòu)下之計時器一種相對簡單的擴充?;氐絎indows以前的MS-DOS程序?qū)懽鳝h(huán)境下,應(yīng)用程式能夠通過攔截者稱為timer tick的BIOS中斷來實現(xiàn)時鐘或計時器。一些為MS-DOS編寫的程序自己攔截這個硬件中斷以實現(xiàn)時鐘和計時器。這些中斷每54.915毫秒產(chǎn)生一次,或者大約每秒18.2次。這是原始的IBM PC的微處理器頻率值4.772720 MHz被218所除而得出的結(jié)果。在Windows 98中,計時器與其下的PC計時器一樣具有55毫秒的解析度。在Microsoft Windows NT中,計時器的解析度為10毫秒。Windows應(yīng)用程式不能以高于這些解析度的頻率(在Windows 98下,每秒18.2次,在Windows NT下,每秒大約100次)接收WM_TIMER消息。在SetTimer中指定的時間間隔總是截尾后tick數(shù)的整數(shù)倍。例如,1000毫秒的間隔除以54.925毫秒,得到18.207個tick,截尾后是18個tick,它實際上是989毫秒。對每個小于55毫秒的間隔,每個tick都會產(chǎn)生一個WM_TIMER消息。 可見,計時器并不能嚴(yán)格按照指定的時間間隔發(fā)送WM_TIMER消息,它總要相差那么幾毫秒。 即使忽略這幾個毫秒的差別,計時器仍然不精確。請看原因二: WM_TIMER消息放在正常的消息隊列之中,和其他消息排列在一起,因此,如果在SetTimer中指定間隔為1000毫秒,那么不能保證程序每1000毫秒或者989毫秒就會收到一個WM_TIMER消息。如果其他程序的執(zhí)行事件超過一秒,在此期間內(nèi),您的程式將收不到任何WM_TIMER訊息。事實上, Windows對WM_TIMER消息的處理非常類似于對WM_PAINT消息的處理,這兩個消息都是低優(yōu)先級的,程序只有在消息隊列中沒有其他消息時才接收它們。 WM_TIMER還在另一方面和WM_PAINT相似:Windows不能持續(xù)向消息隊列中放入多個WM_TIMER訊息,而是將多余的WM_TIMER消息組合成一個消息。因此,應(yīng)用程序不會一次收到多個這樣的消息,盡管可能在短時間內(nèi)得到兩個WM_TIMER消息。應(yīng)用程序不能確定這種處理方式所導(dǎo)致的WM_TIMER消息「遺漏」的數(shù)目。 可見,WM_TIMER消息并不能及時被應(yīng)用程序所處理,WM_TIMER在消息隊列中的延誤可能就不能用毫秒來計算了。 由以上兩點,你不能通過在處理WM_TIMER時一秒一秒計數(shù)的方法來計時。如果要實現(xiàn)一個時鐘程序,可以使用系統(tǒng)的時間函數(shù)如GetLocalTime ,而在時鐘程序中,計時器的作用是定時調(diào)用GetLocalTime獲得新的時間并刷新時鐘畫面,當(dāng)然這個刷新的間隔要等于或小于1秒。 例:1) 不用回調(diào)函數(shù) SetTimer(1,1000,NULL); 1:計時器的名稱; 1000:時間間隔,單位是毫秒; NULL:使用onTime函數(shù)。 當(dāng)不需要計時器的時候調(diào)用KillTimer(nIDEvent); 例如:KillTimer(1); 2) 調(diào)用回調(diào)函數(shù) 此方法首先寫一個如下格式的回調(diào)函數(shù) void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime); 然后再用SetTimer(1,100,TimerProc)函數(shù)來建一個定時器,第三個參數(shù)就是回調(diào)函數(shù)地址。 或許你會問,如果我要加入兩個或者兩個以上的 timer怎么辦? 繼續(xù)用SetTimer函數(shù)吧,上次的timer的ID是1,這次可以是2,3,4。。。。 SetTimer(2,1000,NULL); SetTimer(3,500,NULL); 嗯,WINDOWS會協(xié)調(diào)他們的。當(dāng)然onTimer函數(shù)體也要發(fā)生變化,要在函數(shù)體內(nèi)添加每一個timer的處理代碼: onTimer(nIDEvent) { switch(nIDEvent) { case 1:........; break; case 2:.......; break; case 3:......; break; } } |
|
來自: VoidOc > 《VC VC6.0》