處理函式這裡會花一點時間,如果累的話先去喝杯茶休息一下。
如果對於前面幾篇文章沒很了解,請先去看熟,看熟後接下來就沒什麼了,全都是一些步驟問題而已。
SetTimer 在上一篇有提到整個 API 流程 - (1) SetTimer 建立計數器 (2) 於 CALLBACK TimerProc 執行 (3) KillTimer 刪除計數器。這裡看到有 CALLBACK 函式,在 autoit 開發過程我們就是要先開發 CALLBACK TimerProc。
我們要做的例子是,在 label 上面,每 30ms 的時候把數字加1,加到100之後停下來。整個步驟如下
// ---------------------------------------------------------
// 0. 先把簡單的 GUI 建好
這裡的 GUI 架構長這樣
#include <GUIConstantsEx.au3>
$x = 0 ; 這是我們要累加的指標,當它加到 100 的時候就停下來。
GUICreate("EdisonX", 200,200)
$L1= GUICtrlCreateLabel("", 10, 10, 140, 20) ; 建立 LABEL
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
Exit
EndSelect
WEnd
嗯,看得出來它什麼都沒有,接下來才是重點
// ---------------------------------------------------------
// 1. 建立並註冊 CALLBACK TimerProc
看一下 MSDN 上 CALLBACK TimerProc 原型
VOID CALLBACK TimerProc(
__in HWND hwnd,
__in UINT uMsg,
__in UINT_PTR idEvent, // 只有這個會用到
__in DWORD dwTime
);
傳回 VOID (autoit 用 "none"),第一個參數 hwnd,第二個 uint,第三個也是一個 uint,第四個 DWORD,先寫下自己的 CALLBACK TimerProc,命名是 myTimerProc,寫出來大概長這樣
; 自己寫的 CALLBACK TimerProc Function
Func myTimerProc($hWnd, $uiMsg, $idEvent, $dwTime)
$x = $x+1
GUICtrlSetData($L1, $x)
EndFunc
再註冊,目前程式碼變這樣
#include <GUIConstantsEx.au3>
; 註冊 CALLBACK 函式
$pTimerProc = DllCallbackRegister("myTimerProc", "none", "hwnd;uint;uint;dword")
$x = 0 ; 這是我們要累加的指標,當它加到 100 的時候就停下來。
GUICreate("EdisonX", 200,200)
$L1= GUICtrlCreateLabel("", 10, 10, 140, 20) ; 建立 LABEL
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
Exit
EndSelect
WEnd
; 自己寫的 CALLBACK TimerProc Function
Func myTimerProc($hWnd, $uiMsg, $idEvent, $dwTime)
$x = $x+1
GUICtrlSetData($L1, $x)
EndFunc
// ---------------------------------------------------------
// 2. 呼叫 SetTimer 函式
SetTimer MSDN 上原型如下
UINT_PTR WINAPI SetTimer(
__in_opt HWND hWnd,
__in UINT_PTR nIDEvent,
__in UINT uElapse, // 要設定這個,多久執行一次函式
__in_opt TIMERPROC lpTimerFunc // 把剛剛設好的函式,傳進位址
);
傳回一個 UINT_PTR (uint),第一個參數 (hwnd) 給它垃圾沒差,第二個參數 (uint) 也給它垃圾沒差,第三個參數是要多久執行一次,這裡設 30ms,意思是每 30 ms 就會加 1,第四個參數就傳進剛剛設好 CALLBACK 函式的位址。這部份寫好後原始碼大概長這樣
#include <GUIConstantsEx.au3>
; 註冊 CALLBACK 函式
$pTimerProc = DllCallbackRegister("myTimerProc", "none", "hwnd;uint;uint;dword")
; 調用 SetTimer 函式, 每 30 ms 執行一次 myTimerProc
$myTimer = DllCall("user32.dll", "uint", "SetTimer", _
"hwnd", 0, "uint", 0, "int", 30, "ptr", DllCallbackGetPtr($pTimerProc))
$x = 0 ; 這是我們要累加的指標,當它加到 100 的時候就停下來。
GUICreate("EdisonX", 200,200)
$L1= GUICtrlCreateLabel("", 10, 10, 140, 20) ; 建立 LABEL
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
Exit
EndSelect
WEnd
; 自己寫的 CALLBACK TimerProc Function
Func myTimerProc($hWnd, $uiMsg, $idEvent, $dwTime)
$x = $x+1
GUICtrlSetData($L1, $x)
EndFunc
// ---------------------------------------------------------
// 3. 條件式的呼叫 KillTimer
剛剛我們說過了,這個程式是想要在 $x 跳到 100 的時候就停止計數,這時候就用 KillTimer 把計數器砍掉。KillTimer MSDN 上原型如下
BOOL WINAPI KillTimer(
__in_opt HWND hWnd,
__in UINT_PTR uIDEvent
);
傳回值 (int),參數1 (hwnd),參數2 (uint);要說明的是參數2,因為 Timer 可能不只有一個,假設有10 個,要砍掉哪個 Timer 是用第二個參數去控制,控制方式如下
#include <GUIConstantsEx.au3>
; 註冊 CALLBACK 函式
$pTimerProc = DllCallbackRegister("myTimerProc", "none", "hwnd;uint;uint;dword")
; 調用 SetTimer 函式, 每 30 ms 執行一次 myTimerProc
$myTimer = DllCall("user32.dll", "uint", "SetTimer", _
"hwnd", 0, "uint", 0, "int", 30, "ptr", DllCallbackGetPtr($pTimerProc))
$x = 0 ; 這是我們要累加的指標,當它加到 100 的時候就停下來。
GUICreate("EdisonX", 200,200)
$L1= GUICtrlCreateLabel("", 10, 10, 140, 20) ; 建立 LABEL
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
Exit
EndSelect
WEnd
; 自己寫的 CALLBACK TimerProc Function
Func myTimerProc($hWnd, $uiMsg, $idEvent, $dwTime)
$x = $x+1
GUICtrlSetData($L1, $x)
; 跳到 100 時就不跳了,用 KillTimer 把計數器砍掉。
If $x=100 Then
DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $myTimer[0])
EndIf
EndFunc
// ---------------------------------------------------------
// 4. 再將註冊好的 CALLBACK 釋放
最後一步是用 DllCallBackFree 把剛剛註冊好的 CALLBACK 函式釋放掉,但這裡要注意,剛剛註冊好的 CALLBACK 函式雖然已經暫時沒有要用,但避免以後還有可能會繼續用到,所以應該要放在程式結束時再去釋放它 (除非你非常確定你在某個動作之後不會再調用它,那時你可以將它釋放沒關係),程式碼完整如下
#include <GUIConstantsEx.au3>
; 註冊 CALLBACK 函式
$pTimerProc = DllCallbackRegister("myTimerProc", "none", "hwnd;uint;uint;dword")
; 調用 SetTimer 函式, 每 30 ms 執行一次 myTimerProc
$myTimer = DllCall("user32.dll", "uint", "SetTimer", _
"hwnd", 0, "uint", 0, "int", 30, "ptr", DllCallbackGetPtr($pTimerProc))
$x = 0 ; 這是我們要累加的指標,當它加到 100 的時候就停下來。
GUICreate("EdisonX", 200,200)
$L1= GUICtrlCreateLabel("", 10, 10, 140, 20) ; 建立 LABEL
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
; 程式結束前再把註冊好的 CALLBACK 函式釋放
DllCallbackFree($pTimerProc)
Exit
EndSelect
WEnd
; 自己寫的 CALLBACK TimerProc Function
Func myTimerProc($hWnd, $uiMsg, $idEvent, $dwTime)
$x = $x+1
GUICtrlSetData($L1, $x)
; 跳到 100 時就不跳了,用 KillTimer 把計數器砍掉。
If $x=100 Then
DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $myTimer[0])
EndIf
EndFunc
// ---------------------------------------------------------
// 5. 再加入其它控制進行 Timer 控制
一開始就說過,計數器可以不只一個,這次我們要再多設一個 Timer,它會每秒更新 LABEL 一次,內容為系統目前時間。但這裡要把之前的一些變數名稱都改掉,改掉的原因很簡單,剛剛只有用一個 Timer,用那些變數名稱會讓人比較好理解;但 Timer 一多,變數名稱就要合適。
注意到一點,事實上即使有二個 Timer ,但處理的函式是一樣的(所以只要註冊一次就可以,因為是同樣函式,可以重複使用,因為處理函式可以由參數 nIDEvent 取得是由哪個 Timer 呼叫。
以下是設定的變數名稱與說明
計數器名稱 | 執行週期 | 執行動作(CALLBACK function) | 結束條件(KillTimer) | 對應函式名稱 | 註冊函式指標變數名稱 |
SumTimer | 30ms | 將 $X1+1,顯示在 $L1 上 | $X = 100 | TimerProc | pTimerProc |
GetNowTimer | 1 sec | 在 $L2 顯示現在時間 | 程式結束前 | TimerProc | pTimerProc |
程式碼看起來可能有點亂,這系列文章多看二遍就懂了。
#include <GUIConstantsEx.au3>
#Include <Date.au3>
GUICreate("EdisonX", 200,200)
$L1= GUICtrlCreateLabel("", 10, 10, 140, 20) ; 0~100
$L2= GUICtrlCreateLabel(_now(), 10, 50, 140, 20) ; 顯示時間
$x=0
; 註冊 CALLBACK function, TimerProc
$pTimerProc = DllCallbackRegister("TimerProc", "none", "hwnd;uint;uint;dword")
; 用 SetTimer 開始對 $L1 做計數
$SumTimer = DllCall("user32.dll", "uint", "SetTimer", _
"hwnd", 0, "uint", 0, "int", 30, "ptr", DllCallbackGetPtr($pTimerProc))
; 用 GetNowTimer 開始對 $L2 計數
$GetNowTimer = DllCall("user32.dll", "uint", "SetTimer", _
"hwnd", 0, "uint", 0, "int", 1000, "ptr", DllCallbackGetPtr($pTimerProc))
GUISetState()
While 1
$msg = GUIGetMsg()
Select
Case $msg = $GUI_EVENT_CLOSE
; 離開前將 GetNowerTimer 刪除
DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $GetNowTimer[0])
; 釋放已註冊的 CALLBACK 函式
DllCallbackFree($pTimerProc)
Exit
EndSelect
WEnd
Func TimerProc($hWnd, $uiMsg, $idEvent, $dwTime)
; 使用 $idEvent 判斷是由哪個 Timer 發出來
Switch $idEvent
; 進行累加
case $SumTimer[0]
$x = $x+1
GUICtrlSetData($L1, $x)
; 條件達成,將 SumTimer 刪除
If $x=100 Then
DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $SumTimer[0])
EndIf
; 設定時間
case $GetNowTimer[0]
GUICtrlSetData($L2, _Now())
EndSwitch
EndFunc
DLL 於 autoit 使用於此暫時到此結束。如果有其它問題的話可以留言,有空的時候我再試試。