處理函式這裡會花一點時間,如果累的話先去喝杯茶休息一下。

如果對於前面幾篇文章沒很了解,請先去看熟,看熟後接下來就沒什麼了,全都是一些步驟問題而已。

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 使用於此暫時到此結束。如果有其它問題的話可以留言,有空的時候我再試試。

edisonx 發表在 痞客邦 PIXNET 留言(0) 人氣()