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 [Advanced] Callback & Phương Pháp Đa Luồng Trong AutoIt

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

  1. wuuyi123

    wuuyi123 Thành viên
    • 18/23

    Tham gia ngày:
    18/6/16
    Bài viết:
    51
    Đã được thích:
    87
    I. Callback là gì?
    Bạn có thế tham khảo Wiki để biết nó là thế nào, hơi rắc rối một tí. Để cho dễ hiểu hơn tôi xin được trích dẫn một tí từ bài viết này.

    Đối với AutoIt thì sao? Thực tế thì các hàm chỉ trả về kết quả sau khi thực thi xong (tức là chạy đến Return hoặc hết các dòng code) nên khái niệm Callback quá xa vời. Còn một cách đó là thông qua GUI.

    Nếu bạn code một GUI đơn giản từ Koda hoặc tự code bằng GUICreate thì trong While...GuiGetMsg...WEnd, bạn nghĩ nó là một Callback chăng? Khi nhấn một button thì GUISetMsg sẽ nhận và trả về kết quả ngay. Nhưng nếu bạn đang thực thi một hàm bất kỳ thì nó sẽ dừng tương tác của GUI (tức vòng lặp trên không hoạt động). Suy ra, nó không phải là Callback.

    Đối với GUI/Control tạo từ Win32 thì chắc chắn có Callback. Thế Callback này tồn tại để làm gì? Cũng như một chương trình AutoIt thông thường vậy, nếu bạn không cho vòng lặp thì nó sẽ tự thoát sau khi thực thi xong. Và Callback sẽ nhận các Message từ GUI hoặc User gửi tới để nó xử lý và trả về kết quả. Lấy ví dụ cụ thể như sau

    Mã (AutoIt):
    ; Callback của một GUI
    Func WINPROC($hWnd, $iMsg, $wParam, $lParam)

        Switch $iMsg ; đọc message của GUI gửi tới từ biến $iMsg và xử lý
            Case $WM_CLOSE ; khi nhấn close (nút X) thì sẽ thực thi lệnh dưới
                Exit ; thoát
                EndSwitch
        EndSwitch

        Return _WinAPI_DefWindowProcW($hWnd, $iMsg, $wParam, $lParam)
    EndFunc
    II. Phương pháp đa luồng?
    Đa luồng (MultiThread) có thể được tạo ra bằng cách sử dụng hàm CreateThread (hàm này dùng DllCall(Kernel32.dll...), lên MSDN tìm thêm) để tạo ra một luồng. Nhưng nếu vướng vào vòng lặp thì "đi đai" chắc. Nếu bạn là người thành thạo (tại sao phải thành thạo?) C/C++ thì hãy tạo một file Dll riêng và dùng nó để gọi ra một luồng cho AutoIt, nhưng code trong AutoIt lại không chạy được trên đó. Vì vậy yêu cầu người thành thạo để sử dụng code trong C/C++ cho AutoIt đấy =)). Rất khó khăn phải không nào tôi sẽ viết một bài nói về tạo Dll từ C/C++FreeBasic để gọi và tương tác cho AutoIt sau.

    Trên thực tế thì Callback của Win32 chính là 1 luồng rồi đấy (tham khảo tạo GUI Win32 Callback của nó tại đây), cộng thêm 1 luồng của AutoIt là 2. Nhưng làm sao để tạo thêm ngoài cách tạo GUI từ Win32. Đơn giản lắm, đó là Subclass, CallProc Hook.

    Hook thì quá xa vời với GUI và rất khó sử dụng, nên tôi không hướng dẫn về nó.

    1. Subclass
    Khác với Callback từ tạo GUI (Win32), Subclass sẽ tạo một hàm Callback riêng cho một GUI/Control đã được tạo sẵn, tức là bạn có thể tăng thêm tính tương tác với GUI cơ bản của AutoIt thông qua nó hoặc các Control tưởng chừng như vô dụng.

    Trong WinAPI của AutoIt có một hàm để gọi Subclass, "_WinAPI_SetWindowSubclass".

    Mã (AutoIt):
    ; THư viện không thể thiếu
    #include <WinAPIShellEx.au3>

    ; Đăng kí hàm Callback cho Subclass, với tên hàm là SubclassProc, các parameter thì để y nguyên hoặc tham khảo trên MSDN nhé
    Global $hDll = DllCallbackRegister("SubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    ; Get Pointer (C/C++ cân tất =]])
    Global $pDll = DllCallbackGetPtr($hDll)

    ; Tạo một GUI traditional củ chuối...
    Global $hGUI = GUICreate("Test", 200, 60)

    ; Tạo 2 button để test để Set và Remove Subclass
    Global $Btn1 = GUICtrlCreateButton("Subclass", 15, 25, 75, 25)
    Global $Btn2 = GUICtrlCreateButton("Remove", 110, 25, 75, 25)

    ; Có dòng này để GUI hiện lên chứ, hoặc không cần nếu set $WS_VISIBLE cho style
    GUISetState(@SW_SHOW)

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case -3 ; Thoát
                GUIDelete()
                DllCallbackFree($hDll)
                Exit
            Case $Btn1
                ; Set Subclass cho GUI ngay và luôn, ID cho subclass là 69, số đẹp
                _WinAPI_SetWindowSubclass($hGUI, $pDll, 69)
            Case $Btn2
                ; Remove Subclass cho GUI, ID đã set là 69 nhé
                 _WinAPI_RemoveWindowSubclass($hGUI, $pDll, 69)
        EndSwitch
    WEnd

    ;Callback function: SubclassProc
    Func SubclassProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
        ; Xuất lên Console message của GUI, User khi tương tác với nó, nhớ xem phần console nhé
        ConsoleWrite(@CRLF & "Msg: " & Hex($iMsg, 4))

        ; Luôn có dòng này đề tránh lỗi
        Return _WinAPI_DefSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    EndFunc
    2. CallProc
    CallProc
    cũng tương tự như Subclass, nó sẽ bổ trợ cho Callback chính của GUI.

    Sử dụng hàm "_WinAPI_SetWindowLong" để lấy giá trị Callback chính và set Callback phụ vào. Trong Callback phụ sử dụng giá trị trên Define cho nó.

    Mã (AutoIt):
    ; THư viện không thể thiếu
    #include <WinAPI.au3>
    #include <WinAPIShellEx.au3>

    Global $OldProc

    ; Đăng kí hàm Callback phụ, với tên hàm là NextProc, các parameter thì để y nguyên hoặc tham khảo trên MSDN nhé
    Global $hDll = DllCallbackRegister("NextProc", "ptr", "hwnd;uint;long;ptr")
    ; Get Pointer (C/C++ cân tất =]])
    Global $pDll = DllCallbackGetPtr($hDll)

    ; Tạo một GUI traditional củ chuối...
    Global $hGUI = GUICreate("Test", 200, 60)

    ; Tạo 2 button để test để Set và Remove Subclass
    Global $Btn1 = GUICtrlCreateButton("Subclass", 15, 25, 75, 25)
    Global $Btn2 = GUICtrlCreateButton("Remove", 110, 25, 75, 25)

    ; Set Callback phụ
    $OldProc = _WinAPI_SetWindowLong($hGUI, $GWL_WNDPROC, $pDll)

    ; Có dòng này để GUI hiện lên chứ, hoặc không cần nếu set $WS_VISIBLE cho style
    GUISetState(@SW_SHOW)

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case -3 ; Thoát
                GUIDelete()
                DllCallbackFree($hDll)
                Exit
            Case $Btn1

            Case $Btn2

        EndSwitch
    WEnd

    ;Callback function phụ: NextProc
    Func NextProc($hWnd, $iMsg, $wParam, $lParam, $iID, $pData)
        ; Xuất lên Console message của GUI, User khi tương tác với nó, nhớ xem phần console nhé
        ConsoleWrite(@CRLF & "Msg: " & Hex($iMsg, 4))

        If $iMsg = 0x111 Then ;$WM_Command
            Switch $lParam ; $lParam trả về handle của control vừa tương tác
                Case GUICtrlGetHandle($Btn1)
                    MsgBox(0, "", "Button 1 Clicked")
                Case GUICtrlGetHandle($Btn2)
                    MsgBox(0, "", "Button 2 Clicked")
                ; Vô tư click...
            EndSwitch
        EndIf

        ; Luôn có dòng này đề tránh lỗi
        Return _WinAPI_CallWindowProc($OldProc, $hWnd, $iMsg, $wParam, $lParam)

        ;Dùng hàm dưới để Define (Unicode) thay cho hàm trên (ANSI), nhớ thêm #include <WinAPISys.au3>
        ;Return _WinAPI_CallWindowProcW($hWnd, $iMsg, $wParam, $lParam)
    EndFunc
    III. Kết
    Đa luồng không thể đâu, ở đây chỉ là callback, khi nào main thread rảnh sẽ ưu tiên cho callback chạy, còn không rảnh thì callback đứng chờ thôi. =))
     
    Chỉnh sửa cuối: 26/6/18
  2. Huân Hoàng

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

    Tham gia ngày:
    29/9/15
    Bài viết:
    636
    Đã được thích:
    1,123
    Ví dụ hóm hỉnh dễ hiểu, chủ đề rất đáng để quan tâm và ngâm cứu. Điểm 10 cho chất lượng :autoit:
     
  3. wuuyi123

    wuuyi123 Thành viên
    • 18/23

    Tham gia ngày:
    18/6/16
    Bài viết:
    51
    Đã được thích:
    87
    Thým @Huân Hoàng quá khen rồi, chỉ là mấy cái nhỏ thôi mà. :D
     
  4. Only Love

    Only Love Thành viên
    • 18/23

    Tham gia ngày:
    8/9/15
    Bài viết:
    64
    Đã được thích:
    57
    So với DllCall CreateThread thì code bro wuuyi123 cung cấp chạy rất ổn, không crash vặt, nhưng ngặt cái phải tác động lên GUI (rê chuột, click...) thì nó mới gọi callback. Mình nảy ra ý tưởng là nếu mình không GUISetState(@SW_SHOW) cho 1 GUI trống, muốn gọi thread mới (callback) thì gửi ControlClick đến GUI (Hidden) đó thì cũng khá là ổn chứ nhỉ
     
  5. wuuyi123

    wuuyi123 Thành viên
    • 18/23

    Tham gia ngày:
    18/6/16
    Bài viết:
    51
    Đã được thích:
    87
    Thật sự thì nó không phải là multi-thread gì cả, bản chất của callback chỉ chạy trên một luồng và nó chạy bất chấp ngăn cản (trừ Sleep), nên việc sử dụng loop vào dễ bị crash hoặc ngốn tài nguyên.

    Đối với idea của bạn thì không cần phải dùng đến gui, chỉ cần tạo hàm callback là đủ. Đầu tiên register callback (DllCallbackRegister) cho một hàm để tạo callback, sau đó lấy pointer của nó (DllCallbackGetPtr) rồi gọi hàm thông qua pointer (DllCallAddress).

    Mã (AutoIt):
    $fn = DllCallbackRegister('test', 'none', 'str');
    $lpfn = DllCallbackGetPtr($fn);

    DllCallAddress ($lpfn, 'none', 'str', 'Hello');
    DllCallAddress ($lpfn, 'none', 'str', 'World');

    MsgBox (0, '', 'break');

    func test($text)
        MsgBox(0, '', $text);
    endfunc
     
    Huân Hoàng and Only Love like this.
  6. Only Love

    Only Love Thành viên
    • 18/23

    Tham gia ngày:
    8/9/15
    Bài viết:
    64
    Đã được thích:
    57
    hay quá, đọc bài của bro mình đang nổi máu ghiền vọc vạch, mình sẽ tìm hiểu thêm với những kiến thức bro cung cấp. xin cảm ơn bro.
     
  7. wuuyi123

    wuuyi123 Thành viên
    • 18/23

    Tham gia ngày:
    18/6/16
    Bài viết:
    51
    Đã được thích:
    87
    Bạn có thể xem qua 2 project này, có thể sẽ bổ trợ thêm kiến thức về winapi, callback, real-thread.

    CtrlEvent: https://github.com/wy3/CtrlEvent
    MsgBoxSync: https://github.com/wy3/MsgBoxSync
     
    Huân Hoàng and Only Love like this.

Chia sẻ trang này

Đang tải...