1. Tải bản cài đặt AutoIT mới nhất

    Chào Khách. Nếu bạn mới tham gia và chưa cài đặt AutoIT.
    Vui lòng vào topic trên để tải bản AutoIT mới nhất nhé
    Dismiss Notice
  2. Quy định và nội quy

    Chào Khách. Vui lòng đọc kỹ nội quy và quy định của diễn đàn
    Để tránh bị ban một cách đáng tiếc nhé!
    Dismiss Notice
  3. Hướng dẫn chèn mã AutoIT trong diễn đàn

    Chào Khách. Vui lòng xem qua bài viết này
    Để biết cách chèn mã AutoIT trong diễn đàn bạn nhé :)
    Dismiss Notice

Hướng dẫn Tạo vòng lặp từ WinAPI

Thảo luận trong 'Hướng dẫn - Bài tập nâng cao' bắt đầu bởi wuuyi123, 20/12/17.

  1. wuuyi123

    wuuyi123 Thành viên
    • 18/23

    Tham gia ngày:
    18/6/16
    Bài viết:
    54
    Đã được thích:
    98
    [​IMG]


    - Nhắc đến vòng lặp (Loop) thì rất quen thuộc phải không, cứ for...next hay while...wend hoặc do...until (while-do, for-do). Loop rất hữu ích trong việc lặp đi lặp lại một việc nào đó nhiều lần, thay vì code ra cả trăm dòng thì chỉ cần vài dòng trong Loop là xong ngay.

    Mã (AutoIt):
    ; ví dụ: in ra console các số từ 1 đến 10

    ; - code ra 10 dòng
    ConsoleWrite(@CRLF & 1)
    ConsoleWrite(@CRLF & 2)
    ConsoleWrite(@CRLF & 3)
    ConsoleWrite(@CRLF & 4)
    ConsoleWrite(@CRLF & 5)
    ConsoleWrite(@CRLF & 6)
    ConsoleWrite(@CRLF & 7)
    ConsoleWrite(@CRLF & 8)
    ConsoleWrite(@CRLF & 9)
    ConsoleWrite(@CRLF & 10)

    ; - sử dụng vòng lặp
    For $i = 1 To 10
        ConsoleWrite(@CRLF & $i)
    Next

    ; hoặc
    $i = 1
    Do
        ConsoleWrite(@CRLF & $i)
        $i += 1
    Until $i > 10

    ; hoặc
    $i = 1
    While $i <= 10
        ConsoleWrite(@CRLF & $i)
        $i += 1
    WEnd

    - Tuy nhiên các hàm tạo Loop trong AutoIt rất hạn chế. Khi thực hiện vòng lặp thì chương trình chỉ thao tác duy nhất trong vòng lặp (chạy các code và lặp cho đến khi kết thúc điều kiện hoặc ExitLoop). Điều này rất bất tiện đối với GUI Au3, đang chạy vòng lặp mà chưa biết khi nào nó dừng, cứ nhấn này nhấn kia mà vẫn không thể tương tác được với giao diện, làm phát sinh ra nhiều lỗi hơn.

    - Một điều nữa là quãng nghỉ (Time-Out), cho ví dụ như này: tôi tạo vòng lặp chạy 10 lần, quy định nó thực hiện trong 10s, sẽ code là For.....[làm gì gì đó]...Sleep(1000)...Next, và tất nhiên là nó sẽ không thể hoàn tất trong ~ 10s rồi, cho dù là nghỉ mỗi lần 1s (1000ms) và nghỉ đến tận 10 lần (lý thuyết là vậy :mad:). Bởi vì phần [làm gì gì đó] có thể chứa hàm hay câu lệnh và AutoIt sẽ chờ cho nó thực thi xong mới chạy tiếp tục (Ping(pornhub.com) cũng tốn thời gian mà :D), thêm Sleep() vào làm chương trình dừng lại và có thể phát sinh thêm lỗi. Nếu giải quyết cách này bằng TimerInit cũng vô dụng.

    - Để khắc phục những hạn chế trên thì có cách này rất hữu hiệu mà vẫn ít người biết đến:

    "Sử dụng Timers của Windows"


    - Vừa đọc qua cứ nghĩ là "kêu thằng Windows nó đếm giờ hộ cho mình"? Thế thời gian chờ để cho hàm trả về kết quả thi tính sao?
    - Không phải vậy, bạn có thể tham khảo link này. Phương pháp này sẽ sử dụng các hàm Timers của WinAPI để gọi ra bộ đếm thời gian tương tác với một hàm "Gái Gọi" ý nhầm hàm Callback :D và thực hiện hàm đó lặp đi lặp lại nhiều lần như trong một vòng lặp. Tất nhiên là hàm đó sẽ thực thi các lệnh liên tục mà không cần phải đợi trả về kết quả, miễn là đúng giờ giấc. Và cái này cũng được xem là một luồng (Thread) độc lập và ưa ái đến từ Windows =)).

    - Hướng dẫn chi tiết kèm ví dụ cơ bản, đọc từ trên dài xuống. (xem bên dưới hoặc copy code về hoặc link này)

    Mã (AutoIt):
    #cs Khai báo và cấu trúc hàm callback của timer.

    Global $hTimerProc = DllCallbackRegister('<Tên hàm>', 'none', 'hwnd;uint_ptr;uint;dword')

    Func <Tên hàm> ($hWnd, $iMsg, $iTimerID, $iTime)
        ; -----------------------------------------------------------

        ; $hWnd     (HWND): là giá trị handle của cửa sổ được set timer.
        ; $iMsg      (INT): thông điệp dạng số nguyên được gửi từ WM_TIMER trong callback của cửa sổ.
        ; $iTimerID  (INT): ID timer.
        ; $iTime     (UINT): Thời gian (mili giây) từ khi timer bắt đầu đếm cho mỗi lần thực thi, sai số rất nhỏ.

        ; do some thing...

        ; -----------------------------------------------------------

    EndFunc

    #ce


    #cs
    Tên hàm.........:    SetTimer
    Mô tả:..........:    Tạo ra một timer dựa trên handle của cửa sổ.
    Cú pháp.........:    SetTimer($hWnd, $iTimerID, $iElapse, $pTimerFunc)
    Tham số.........:    -     $hWnd        (HWND): handle của cửa sổ, nếu không có cửa sổ (GUI) nào thì để là 0 (Desktop).
                        -    $iTimerID   (INT): định trước mã ID của một timer, không có tác dụng nếu trùng với các timer đã tạo trước (cùng ID, cùng HWND).
                        -    $iElapse   (UINT): giá trị thời gian chờ (time-out giữa các lần thực thi).
                        -    $pTimerFunc (PTR): pointer của hàm callback đã được khai báo trước đó. Nếu giá trị này là NULL thì timer sẽ gọi trong
                            hàm callback của cửa sổ với message WM_TIMER (0x0113). Lấy pointer bằng hàm DllCallbackGetPtr()

    Giá trị trả về..:    -    (UINT) ID timer.
    #ce
    ==================================================
    Func SetTimer($hWnd, $iTimerID, $iElapse, $pTimerFunc)
        Local $aRet = DllCall('user32.dll', 'uint_ptr', 'SetTimer', _
                'hwnd',     $hWnd, _
                'uint_ptr', $iTimerID, _
                'uint',     $iElapse, _
                'struct*',     $pTimerFunc)

        If @error Then Return SetError(@error, @extended, 0)
        Return $aRet[0]
    EndFunc

    #cs
    Tên hàm.........:    KillTimer
    Mô tả:..........:    Đóng một timer đã gọi trước đó thông qua handle cửa sổ và ID timer.
    Cú pháp.........:    KillTimer($hWnd, $iTimerID)
    Tham số.........:    -     $hWnd        (HWND): handle của cửa sổ.
                        -    $iTimerID   (INT): mã ID đã tạo ra trước đó từ hàm SetTimer().
    Giá trị trả về..:    -    (BOOL): true/false.
    #ce
    ===========================
    Func KillTimer($hWnd, $iTimerID)
        Local $aRet = DllCall('user32.dll', 'bool', 'KillTimer', _
                'hwnd',     $hWnd, _
                'uint_ptr', $iTimerID)

        If @error Then Return SetError(@error, @extended, 0)
        Return $aRet[0]
    EndFunc

    #cs
        Thực chất 2 hàm trên đều có trong thư viện WinAPI AutoIt
        _WinAPI_SetTimer & _WinAPI_KillTimer
    #ce






    #Region EXAMPLE #############################################################

    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>

    ;========== Ví dụ 1 ===============================
    ; tạo biến số, tương tự như biến đếm trong for-next.
    Global $iData = 0
    ; đăng kí/khai báo hàm callback TimerProc.
    Global $hTimerProc = DllCallbackRegister('TimerProc', 'none', 'hwnd;uint_ptr;uint;dword')

    ; tạo timer, ID là 69, quãng nghỉ là 500 ms, hàm callback là TimerProc.
    SetTimer(0, 69, 500, DllCallbackGetPtr($hTimerProc))

    ; tạo vòng lặp để chương trình không bị tắt.
    ; bỏ cái này để sang ví dụ 2 ngay, vẫn tương tác được với GUI trong vd2 luôn mặc dù nó đang chạy, nếu là Loop của AutoIt sẽ không phản hồi.
    While 1
        ; đk thoát while và sang vd2 ngay và luôn.
        If $iData > 10 Then
            ExitLoop ;
        EndIf

        Sleep(10) ; nhớ thêm sleep() để không bị lỗi ngốn CPU.
    WEnd

    Func TimerProc($hWnd, $iMsg, $iTimerID, $iTime)
        #forceref $hWnd, $iMsg, $iTimerID, $iTime

        $iData += 1 ; tăng biến lên 1, tương tự như while-wend hoặc do-until.

        ConsoleWrite(@CRLF & '=== ' & $iData & ' ===') ; ghi lên console.

    EndFunc

    ;========== Ví dụ 2 ========================================================
    ; Trong vd này, bạn hoàn toàn có thể tương tác với GUI trong khi vòng lặp vẫn đang chạy.
    ; Bình thường, khi kéo thả GUI thì nó sẽ dừng lại, nhưng cái này thì KHÔNG!!!
    Global $hTimerProc2 = DllCallbackRegister('TimerProc2', 'none', 'hwnd;uint_ptr;uint;dword')

    #Region ### GUI ###
    Global $hForm = GUICreate("Timers WinAPI + GUI", 300, 200, -1, -1)
    Global $idBtn = GUICtrlCreateButton("Start", 10, 10, 75, 25)
    Global $BtnState = False

    GUISetState(@SW_SHOW)
    #EndRegion

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE
                Exit
            Case $idBtn
                If $BtnState Then
                    KillTimer($hForm, 70)
                    GUICtrlSetData($idBtn, "Start")
                Else
                    SetTimer($hForm, 70, 100, DllCallbackGetPtr($hTimerProc2))
                EndIf
                $BtnState = Not $BtnState

        EndSwitch
    WEnd

    Func TimerProc2($hWnd, $iMsg, $iTimerID, $iTime)
        #forceref $hWnd, $iMsg, $iTimerID, $iTime

        GUICtrlSetData($idBtn, Random(0, 999, 1))

    EndFunc

    #EndRegion ########################################################
    - Trên chỉ là ví dụ cơ bản thôi. Khi thành thạo việc tạo Loop từ Timers sẽ giúp bạn rất nhiều thứ mà Loop thông thường không làm đươc. Có thể làm bộ đếm thời gian, hệ thống auto-update cho phần mềm, hay đồng bộ hóa (Synchronize). Chúc bạn thành công!
     
    Chỉnh sửa cuối: 20/12/17
  2. Huân Hoàng

    Huân Hoàng Administrator Thành viên BQT Administrator Super Moderator
    • 93/113

    Tham gia ngày:
    29/9/15
    Bài viết:
    642
    Đã được thích:
    1,181
    Tuyệt vời, iêm đang băn khoăn không biết làm gì với để sửa cái anti-debugger của iêm (dùng hook mouse code chạy giật tung chảo), đọc bài này như nắng hạn gặp mưa rào, đã giải quyết được vấn đề :D
     
    minhvnnhut thích bài này.
  3. kakanet

    kakanet Thành viên năng động
    • 43/45

    Tham gia ngày:
    24/2/16
    Bài viết:
    203
    Đã được thích:
    211
    bài viết rất hay và bổ ích, chạy có vẻ ko bị crash như winapi CreateThread, hình như đã được thớt tinh chỉnh gì đó
     
    Huân Hoàng thích bài này.
  4. wuuyi123

    wuuyi123 Thành viên
    • 18/23

    Tham gia ngày:
    18/6/16
    Bài viết:
    54
    Đã được thích:
    98
    Thêm tí nuối vào để nó mặn hơn. :D
     
    Huân Hoàng thích bài này.
  5. dechut

    dechut Thành viên mới
    • 3/6

    Tham gia ngày:
    1/8/16
    Bài viết:
    20
    Đã được thích:
    16
    siêu cấp khủng khiếp , cảm ơn bác đã mở ra cho em 1 chân trời mới .
     

Chia sẻ trang này

Đang tải...