概述
Windows 的定時器是一種輸入設備,它週期性地在指定的間隔時間通知應用程序。它可以用向指定窗口發送 WM_TIMER 消息或者調用指定的過程來執行用戶的程序。定時器的應用主要包括下面一些地方:
1.時鐘程序 - 顯然,這是定時器最直接的應用。
2.多任務 - 如果程序有大量的數據處理,除了用多線程的辦法,還可以用定時器,在每一個定時器消息中處理一小塊內容。
3.定時顯示程序的狀況 - 定時器就相當於 Dos 編程中的自己掛接在 int 1ch 上面的要定時處理的程序,它可以定時顯示程序運行的情況,如發送了多少內容,接收了多到內容等等。
在遊戲程序中使用定時器可以消除在不同處理器下用延時來保持速度一致所造成的誤差。
4.用於數據流處理 - 在音頻、視頻的播放中,需要隔一段時間處理一段數據。
總的來說,在 Dos 下實現精確定時的唯一方法是在 int 1ch 時鐘中斷中處理程序,但你使用起來必須遵守很多的規範,而在 Windows 的定時器中,你可以用 SetTimer 函數分配不止一個的定時器,比如說,在你的文本編輯程序中,你可以使用一個間隔1秒的定時器來在狀態欄中顯示時鐘,同時分配一個10分鐘的定時器來實現定時存盤的功能。定時器實際上是 Windows 對時鐘中斷的一種擴展,它的本質還是基於時鐘中斷的,所以你實際上無法把定時器的間隔設置到55毫秒以下,另外,定時器的精度也是以55毫秒爲倍數的,比如說,你設置了一個1秒的定時器,它實際上是在每989毫秒的時候發生的。和在 Dos 下使用時鐘中斷,windows 的定時器還有下面一些要點:
1.在 Dos 中,你的程序隨時可能被 int 1ch 打斷,而在Windows 中,Windows 通過 WM_TIMER 消息把定時器消息放入正常的消息隊列中,所以你不必擔心你的程序在別的處理中被定時器打斷。
2.不可能有同時兩條以上的 WM_TIMER 消息,如果在一個還在消息隊列中,窗口再得到一條 WM_TIMER 消息,兩條消息會被合併爲一條,所以在程序比較忙的時候可能會丟失 WM_TIMER 消息。
WM_TIMER 消息的級別是很低的,程序只有在消息隊列中沒有其他消息的情況下,纔會接收 WM_TIMER 消息,你可以通過下馬方法驗證:在一個設置了定時器的窗口上按住標題欄移動窗口,你會發現定時器停止了工作,當你鬆開鼠標後,在這個過程中丟失的 WM_TIMER 消息並沒有被補上,所以如果你設計一個時鐘程序,你不能使用定時器消息來計數,而必須在消息中每次獲取正確的系統時間。
講了這麼多定時器的特點,下面是定時器相關的API,你會發現除了在使用中要注意的這些特性,定時器的API真是又少又簡單:
建立定時器
SetTimer(
HWND hWnd, // handle of window for timer messages
UINT nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // address of timer procedure
);
hWnd 是 windows 發送 WM_TIMER 的窗口,nIDEvent 是定時器的編號,在 WM_TIMER 中出現在 wParam 參數中,用來區分在多個定時器的情況下,這條消息是由哪個定時器產生的。uElapse 是定時器間隔的毫秒數,如果你要設置一個1秒的定時器,這個值就是1000,lpTimerFunc 是處理定時器消息的過程,如果這個參數不是 NULL,windows 在到時間後會調用lpTimerFunc 指定的過程,調用的參數是 CALLBACK TimerProc(hwnd,WM_TIMER,iTimerID,dwTime),iTimerID 是定時器 ID,dwTime 是系統時間;如果 lpTimerFunc 參數是 NULL,Windows 會把 WM_TIMER 消息放入消息循環中,消息的 hWnd 是第一個參數中指定的 hWnd,也就是說向這個窗口發送了 WM_TIMER 消息。
另外,如果你的程序沒有窗口,你也可以用這種辦法建立定時器:invoke SetTimer,NULL,NULL,uElapse,TimerProc,函數會返回一個系統定義的 TimerID供你在 KillTimer 中使用。
取消定時器
KillTimer(
HWND hWnd, // handle of window that installed timer
UINT uIDEvent // timer identifier
);
取消定時器只需對應 SetTimer 時的 hWnd 和 uIDEvent 調用 KillTimer 函數就行了。
在本節的例子程序中,我在對話框中的 WM_INIT 消息中用 SetTimer 建立兩個定時器,時間分別是500ms 和 200ms,然後在間隔0.5秒的定時器消息中更換按鈕上的圖片,在間隔 0.2 秒的定時器消息中更換標題欄上的小圖標,你就可以看到動畫的效果了。
源程序 - 彙編源文件
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Programmed by 羅雲彬, [email protected]
; Website: http://win32asm.com.cn
; LuoYunBin's Win32 ASM page (羅雲彬的編程樂園)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 版本信息
; 彙編教程附帶例子程序 - 定時器的使用
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat, stdcall
option casemap :none ; case sensitive
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 數據
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
include kernel32.inc
include comctl32.inc
include comdlg32.inc
includelib user32.lib
includelib kernel32.lib
includelib comctl32.lib
includelib comdlg32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 數據
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDI_MAIN equ 1
IDI_MOON1 equ 2
IDI_MOON2 equ 3
IDI_MOON3 equ 4
IDI_MOON4 equ 5
IDI_MOON5 equ 6
IDI_MOON6 equ 7
IDI_MOON7 equ 8
IDI_MOON8 equ 9
DLG_MAIN equ 1000
ID_MOON equ 1001
ID_TIMER1 equ 1
ID_TIMER2 equ 2
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數據段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
dwCounter1 dd ?
dwCounter2 dd ?
hIcon1 dd ?
hIcon2 dd ?
hIcon3 dd ?
hIcon4 dd ?
hIcon5 dd ?
hIcon6 dd ?
hIcon7 dd ?
hIcon8 dd ?
szBuffer db 256 dup (?)
.data
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;********************************************************************
_ProcDlgMain proc uses ebx edi esi, /
hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
mov eax,wMsg
;********************************************************************
.if eax == WM_CLOSE
invoke KillTimer,hWnd,ID_TIMER1
invoke KillTimer,hWnd,ID_TIMER2
invoke EndDialog,hWnd,NULL
;********************************************************************
.elseif eax == WM_INITDIALOG
mov edi,offset hIcon1
mov ebx,IDI_MOON1
mov ecx,8
@@:
push ecx
invoke LoadIcon,hInstance,ebx
cld
stosd
inc ebx
pop ecx
loop @B
invoke SetTimer,hWnd,ID_TIMER1,500,NULL
invoke SetTimer,hWnd,ID_TIMER2,200,NULL
invoke SendMessage,hWnd,WM_SETICON,ICON_SMALL,hIcon1
;********************************************************************
.elseif eax == WM_TIMER
.if wParam == ID_TIMER1
inc dwCounter1
.if dwCounter1 == 8
mov dwCounter1,0
.endif
mov eax,dwCounter1
shl eax,2
add eax,offset hIcon1
mov eax,[eax]
invoke SendMessage,hWnd,WM_SETICON,ICON_SMALL,eax
.else
inc dwCounter2
.if dwCounter2 == 8
mov dwCounter2,0
.endif
mov eax,dwCounter2
shl eax,2
add eax,offset hIcon1
mov eax,[eax]
invoke SendDlgItemMessage,hWnd,ID_MOON,BM_SETIMAGE,IMAGE_ICON,eax
.endif
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
;********************************************************************
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,0
invoke ExitProcess,NULL
;********************************************************************
end start
程序的分析和要點
有了上面的介紹,這個程序是很容易看懂的,在 WM_TIMER 消息中,通過 wParam 中的 TimerID 可以區分是哪個定時器產生的消息。在 WM_CLOSE 消息中,通過 KillTimer 來取消定時器。本程序中的的圖標定義在資源文件中,在對話框建立的時候,先用 LoadIcon 裝入,然後爲兩個定時器分別保存一個圖片編號 dwCounter1 和 dwCounter2,在定時器消息中分別用 WM_SETICON 和 BM_SETIMAGE 消息來對窗口標題的圖標和按鈕的圖標進行設置。
本文來自CSDN博客,出處:http://blog.csdn.net/PigZJ/archive/2009/05/06/4153769.aspx