Win32彙編教程十 定時器的應用 (轉)

gugu99發表於2007-12-29
Win32彙編教程十 定時器的應用 (轉)[@more@]




--------------------------------------------------------------------------------

在這兒本節的所有源

概述

的定時器是一種輸入裝置,它週期性地在指定的間隔時間通知應用程式。它可以用向指定視窗傳送 WM_TIMER 訊息或者指定的過程來的程式。定時器的應用主要包括下面一些地方:

時鐘程式 - 顯然,這是定時器最直接的應用。
多工 - 如果程式有大量的資料處理,除了用多執行緒的辦法,還可以用定時器,在每一個定時器訊息中處理一小塊內容。
定時顯示程式的狀況 - 定時器就相當於 D中的自己掛接在 int 1ch 上面的要定時處理的程式,它可以定時顯示程式執行的情況,如傳送了多少內容,接收了多到內容等等。
在遊戲程式中使用定時器可以消除在不同下用延時來保持速度一致所造成的誤差。
用於資料流處理 - 在、影片的中,需要隔一段時間處理一段資料。
總的來說,在 Dos 下實現精確定時的唯一方法是在 int 1ch 時鐘中斷中處理程式,但你使用起來必須遵守很多的規範,而在 Windows 的定時器中,你可以用 SetTimer 分配不止一個的定時器,比如說,在你的文字編輯程式中,你可以使用一個間隔1秒的定時器來在狀態列中顯示時鐘,同時分配一個10分鐘的定時器來實現定時存檔的功能。定時器實際上是 Windows 對時鐘中斷的一種擴充套件,它的本質還是基於時鐘中斷的,所以你實際上無法把定時器的間隔設定到55毫秒以下,另外,定時器的精度也是以55毫秒為倍數的,比如說,你設定了一個1秒的定時器,它實際上是在每989毫秒的時候發生的。和在 Dos 下使用時鐘中斷,windows 的定時器還有下面一些要點:

在 Dos 中,你的程式隨時可能被 int 1ch 打斷,而在Windows 中,Windows 透過 WM_TIMER 訊息把定時器訊息放入正常的訊息佇列中,所以你不必擔心你的程式在別的處理中被定時器打斷。
不可能有同時兩條以上的 WM_TIMER 訊息,如果在一個還在訊息佇列中,視窗再得到一條 WM_TIMER 訊息,兩條訊息會被合併為一條,所以在程式比較忙的時候可能會丟失 WM_TIMER 訊息。
WM_TIMER 訊息的級別是很低的,程式只有在訊息佇列中沒有其他訊息的情況下,才會接收 WM_TIMER 訊息,你可以透過下馬方法驗證:在一個設定了定時器的視窗上按住標題欄移動視窗,你會發現定時器停止了工作,當你鬆開滑鼠後,在這個過程中丟失的 WM_TIMER 訊息並沒有被補上,所以如果你設計一個時鐘程式,你不能使用定時器訊息來計數,而必須在訊息中每次獲取正確的時間。
講了這麼多定時器的特點,下面是定時器相關的,你會發現除了在使用中要注意的這些特性,定時器的API真是又少又簡單:

建立定時器
SetTimer(
HWND hWnd, // handle of window for timer messages
UINT nvent, // 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 羅雲彬, bigluo@telekbird.com.cn
; site:
; LuoYunBin's 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 256 dup (?)

.data

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 程式碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.code
;********************************************************************
_ProcDlgMain proc uses ebx edi esi,
hWnd:D,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
l @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 訊息來對視窗標題的圖示和按鈕的圖示進行設定。

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-996033/,如需轉載,請註明出處,否則將追究法律責任。

相關文章