Jump to content

Tapping into the AutoIT3 event trapping handler.


 Share

Recommended Posts

Hi folks,

I've been happily abusing this forum resources (and many others as well) for over three years now to get stuff done in AutoIT3.

I'm currently stuck at a point where i need to fake some sort of multithreading.

I know AutoIT3 does not and will not support it, but if there only was a good way to tap into the full event trapping cycle of the AU3 engine, i could at least add in some of the routines that need a checkup.

I figured so far that using the GuiOnEventMode and TrayOnEventMode should allow me to give me this opportunity, but unfortunately, it doesn't.

I have extended the existing Au3 examples for both options with a counter to show what i mean:

#include <GUIConstantsEx.au3>
#include <Constants.au3>
#NoTrayIcon
Global $x = 1

Example()


Func Example()
    Opt("GUICoordMode", 2)
    Opt("GUIResizeMode", 1)
    Opt("GUIOnEventMode", 1)

    Opt("TrayOnEventMode", 1)
    Opt("TrayMenuMode", 1) ; Default tray menu items (Script Paused/Exit) will not be shown.

    TrayCreateItem("Exit")
    TrayItemSetOnEvent(-1, "ExitEvent")

    TraySetOnEvent($TRAY_EVENT_PRIMARYDOUBLE, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYDOUBLE, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_MOUSEOVER, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_PRIMARYDOWN, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_PRIMARYUP, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYDOWN, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYUP, "SpecialEvent")

    TraySetState()

    Local $WinHandle = GUICreate("Parent1")
    ConsoleWrite("WinHandle:" & Hex($WinHandle, 8) & @CRLF)
    GUISetOnEvent($GUI_EVENT_CLOSE, "SpecialEvents")
    GUISetOnEvent($GUI_EVENT_MINIMIZE, "SpecialEvents")
    GUISetOnEvent($GUI_EVENT_RESTORE, "SpecialEvents")


    GUICtrlCreateButton("OK", 10, 30, 50)
    GUICtrlSetOnEvent(-1, "OKPressed")

    GUICtrlCreateButton("Cancel", 0, -1)
    GUICtrlSetOnEvent(-1, "CancelPressed")

    GUICtrlCreateEdit("A small text box with some text in it to test the scrollbar clicking" & @CRLF & @CRLF & @CRLF & @CRLF & "and a couple of lines, click and drag the scrollbars to stop the counter", 80, 80, 180, 100)
    GUICtrlSetOnEvent(-1, "EditTouched")

    GUISetState(@SW_SHOW)


    ; Just idle around
    While 1
        Sleep(500)

        ConsoleWrite("Testing message loop:" & $x & @CRLF)
        $x += 1
    WEnd
EndFunc   ;==>Example


Func EditTouched()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "OK Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>EditTouched

Func OKPressed()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "OK Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>OKPressed


Func CancelPressed()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "Cancel Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>CancelPressed


Func SpecialEvents()

    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1

    Select
        Case @GUI_CtrlId = $GUI_EVENT_CLOSE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Close Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)
            Exit

        Case @GUI_CtrlId = $GUI_EVENT_MINIMIZE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Window Minimized", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)

        Case @GUI_CtrlId = $GUI_EVENT_RESTORE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Window Restored", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)

    EndSelect

EndFunc   ;==>SpecialEvents


Func SpecialEvent()
    Select
        Case @TRAY_ID = $TRAY_EVENT_PRIMARYDOUBLE
            MsgBox(64, "SpecialEvent-Info", "Primary mouse button double clicked.")
        Case @TRAY_ID = $TRAY_EVENT_SECONDARYUP
            MsgBox(64, "SpecialEvent-Info", "Secondary mouse button clicked.")
    EndSelect
EndFunc   ;==>SpecialEvent


Func ExitEvent()
    Exit
EndFunc   ;==>ExitEvent

The counter keeps updating the console output, until you hover above a Tray context menu or you click and hold down any of the scrollbars.

The counter that i am running in the background here is in my real life a network process that needs to clear a message buffer from time to time.
I have two applications in Autoit3 running, one server that is doing all the network activity and no GUI related stuff at all and i attach a client to it that has to exchange messages with the server to at least be able to command it up to some level.

However whenever that message buffer of this client stalls, the server that is broadcasting the ACK messages will also stall eventually because his buffers do not get cleared.

I have considered using an Sqlite database in WAL mode to exchange messages between the two apps and even if that can work mighty fast, it looks clumsy, but it ensures me that my client app will not crash on partial messages or miss out on messages.

But if anyone can guide me to a trick where i could at least have all functions called in a processing loop without the AutoIT engine being able to stall that process, in other words:how can i keep that counter "running" at all cost?

Thanks in advance!

Link to comment
Share on other sites

lbsl, A trick you can use here is to install a hook procedure to catch windows messages. Since windows messages are generated all the time, this will give you an always running loop.

Advantages of this solution are:

  • Very easy to implement and a generally applicable solution.
  • Low impact on your script: No additional globals, only a few lines of extra code.
  • No measurable impact on performance: These functions are small efficient functions.
In this example I have added 9 lines to your original code (comment and blank lines not included):

#include <GUIConstantsEx.au3>
#include <Constants.au3>
#include <WinAPI.au3>
#NoTrayIcon
Global $x = 1

Example()


Func Example()
    Opt("GUICoordMode", 2)
    Opt("GUIResizeMode", 1)
    Opt("GUIOnEventMode", 1)

    Opt("TrayOnEventMode", 1)
    Opt("TrayMenuMode", 1) ; Default tray menu items (Script Paused/Exit) will not be shown.

    TrayCreateItem("Exit")
    TrayItemSetOnEvent(-1, "ExitEvent")

    TraySetOnEvent($TRAY_EVENT_PRIMARYDOUBLE, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYDOUBLE, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_MOUSEOVER, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_PRIMARYDOWN, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_PRIMARYUP, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYDOWN, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYUP, "SpecialEvent")

    TraySetState()

    Local $WinHandle = GUICreate("Parent1")
    ConsoleWrite("WinHandle:" & Hex($WinHandle, 8) & @CRLF)
    GUISetOnEvent($GUI_EVENT_CLOSE, "SpecialEvents")
    GUISetOnEvent($GUI_EVENT_MINIMIZE, "SpecialEvents")
    GUISetOnEvent($GUI_EVENT_RESTORE, "SpecialEvents")


    GUICtrlCreateButton("OK", 10, 30, 50)
    GUICtrlSetOnEvent(-1, "OKPressed")

    GUICtrlCreateButton("Cancel", 0, -1)
    GUICtrlSetOnEvent(-1, "CancelPressed")

    GUICtrlCreateEdit("A small text box with some text in it to test the scrollbar clicking" & @CRLF & @CRLF & @CRLF & @CRLF & "and a couple of lines, click and drag the scrollbars to stop the counter", 80, 80, 180, 100)
    GUICtrlSetOnEvent(-1, "EditTouched")

    ; Catch window messages
    Local $hMessageHandler = DllCallbackRegister( "MessageHandler", "long", "int;wparam;lparam" )
    Local $hMessageHook = _WinAPI_SetWindowsHookEx( $WH_GETMESSAGE, DllCallbackGetPtr( $hMessageHandler ), 0, _WinAPI_GetCurrentThreadId() )

    GUISetState(@SW_SHOW)


    ; Just idle around
    While 1
        Sleep(500)

        ConsoleWrite("Testing message loop:" & $x & @CRLF)
        $x += 1
    WEnd

    _WinAPI_UnhookWindowsHookEx( $hMessageHook )
EndFunc   ;==>Example


Func EditTouched()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "OK Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>EditTouched

Func OKPressed()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "OK Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>OKPressed


Func CancelPressed()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "Cancel Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>CancelPressed


Func SpecialEvents()

    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1

    Select
        Case @GUI_CtrlId = $GUI_EVENT_CLOSE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Close Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)
            Exit

        Case @GUI_CtrlId = $GUI_EVENT_MINIMIZE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Window Minimized", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)

        Case @GUI_CtrlId = $GUI_EVENT_RESTORE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Window Restored", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)

    EndSelect

EndFunc   ;==>SpecialEvents


Func SpecialEvent()
    Select
        Case @TRAY_ID = $TRAY_EVENT_PRIMARYDOUBLE
            MsgBox(64, "SpecialEvent-Info", "Primary mouse button double clicked.")
        Case @TRAY_ID = $TRAY_EVENT_SECONDARYUP
            MsgBox(64, "SpecialEvent-Info", "Secondary mouse button clicked.")
    EndSelect
EndFunc   ;==>SpecialEvent


Func ExitEvent()
    Exit
EndFunc   ;==>ExitEvent


Func MessageHandler( $nCode, $wParam, $lParam )
    Local Static $y = 1
    ConsoleWrite("Testing hook message handler: " & $y & @CRLF)
    $y += 1
EndFunc

You can use a timer in the message handler function to execute code at specified intervals.

Link to comment
Share on other sites

Might want to have a look at the Mailslot UDF by Trancexx, I have used it

with much success for inter-script communication, even across a network.

 

Thanks dmob,

It sounds like a more graceful idea than using a database file to frequently poll for messages.

The GUI client and the proxy-server however will not be used on different computers, so i don't necessarily need the network connection for that.

But something i will keep in mind in case i have no other option.

 

lbsl, A trick you can use here is to install a hook procedure to catch windows messages. Since windows messages are generated all the time, this will give you an always running loop.

Advantages of this solution are:

  • Very easy to implement and a generally applicable solution.
  • Low impact on your script: No additional globals, only a few lines of extra code.
  • No measurable impact on performance: These functions are small efficient functions.
In this example I have added 9 lines to your original code (comment and blank lines not included):

#include <GUIConstantsEx.au3>
#include <Constants.au3>
#include <WinAPI.au3>
#NoTrayIcon
Global $x = 1

Example()


Func Example()
    Opt("GUICoordMode", 2)
    Opt("GUIResizeMode", 1)
    Opt("GUIOnEventMode", 1)

    Opt("TrayOnEventMode", 1)
    Opt("TrayMenuMode", 1) ; Default tray menu items (Script Paused/Exit) will not be shown.

    TrayCreateItem("Exit")
    TrayItemSetOnEvent(-1, "ExitEvent")

    TraySetOnEvent($TRAY_EVENT_PRIMARYDOUBLE, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYDOUBLE, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_MOUSEOVER, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_PRIMARYDOWN, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_PRIMARYUP, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYDOWN, "SpecialEvent")
    TraySetOnEvent($TRAY_EVENT_SECONDARYUP, "SpecialEvent")

    TraySetState()

    Local $WinHandle = GUICreate("Parent1")
    ConsoleWrite("WinHandle:" & Hex($WinHandle, 8) & @CRLF)
    GUISetOnEvent($GUI_EVENT_CLOSE, "SpecialEvents")
    GUISetOnEvent($GUI_EVENT_MINIMIZE, "SpecialEvents")
    GUISetOnEvent($GUI_EVENT_RESTORE, "SpecialEvents")


    GUICtrlCreateButton("OK", 10, 30, 50)
    GUICtrlSetOnEvent(-1, "OKPressed")

    GUICtrlCreateButton("Cancel", 0, -1)
    GUICtrlSetOnEvent(-1, "CancelPressed")

    GUICtrlCreateEdit("A small text box with some text in it to test the scrollbar clicking" & @CRLF & @CRLF & @CRLF & @CRLF & "and a couple of lines, click and drag the scrollbars to stop the counter", 80, 80, 180, 100)
    GUICtrlSetOnEvent(-1, "EditTouched")

    ; Catch window messages
    Local $hMessageHandler = DllCallbackRegister( "MessageHandler", "long", "int;wparam;lparam" )
    Local $hMessageHook = _WinAPI_SetWindowsHookEx( $WH_GETMESSAGE, DllCallbackGetPtr( $hMessageHandler ), 0, _WinAPI_GetCurrentThreadId() )

    GUISetState(@SW_SHOW)


    ; Just idle around
    While 1
        Sleep(500)

        ConsoleWrite("Testing message loop:" & $x & @CRLF)
        $x += 1
    WEnd

    _WinAPI_UnhookWindowsHookEx( $hMessageHook )
EndFunc   ;==>Example


Func EditTouched()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "OK Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>EditTouched

Func OKPressed()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "OK Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>OKPressed


Func CancelPressed()
    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1
    MsgBox(0, "Cancel Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle & " CtrlHandle=" & @GUI_CtrlHandle)
EndFunc   ;==>CancelPressed


Func SpecialEvents()

    ConsoleWrite("Testing message loop:" & $x & @CRLF)
    $x += 1

    Select
        Case @GUI_CtrlId = $GUI_EVENT_CLOSE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Close Pressed", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)
            Exit

        Case @GUI_CtrlId = $GUI_EVENT_MINIMIZE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Window Minimized", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)

        Case @GUI_CtrlId = $GUI_EVENT_RESTORE
            ConsoleWrite("Testing message loop:" & $x & @CRLF)
            $x += 1
            MsgBox(0, "Window Restored", "ID=" & @GUI_CtrlId & " WinHandle=" & @GUI_WinHandle)

    EndSelect

EndFunc   ;==>SpecialEvents


Func SpecialEvent()
    Select
        Case @TRAY_ID = $TRAY_EVENT_PRIMARYDOUBLE
            MsgBox(64, "SpecialEvent-Info", "Primary mouse button double clicked.")
        Case @TRAY_ID = $TRAY_EVENT_SECONDARYUP
            MsgBox(64, "SpecialEvent-Info", "Secondary mouse button clicked.")
    EndSelect
EndFunc   ;==>SpecialEvent


Func ExitEvent()
    Exit
EndFunc   ;==>ExitEvent


Func MessageHandler( $nCode, $wParam, $lParam )
    Local Static $y = 1
    ConsoleWrite("Testing hook message handler: " & $y & @CRLF)
    $y += 1
EndFunc
You can use a timer in the message handler function to execute code at specified intervals.

 

 

Thanks Lars,

This indeed works fine. It even looks like i can drop the whole GuiEventMode and TrayEventMode idea with this. (Which saves me a lot of time because i haven't converted any of the dialog message handlers yet to work with the GuiEventMode)

I owe you a big one!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...