Jump to content

GUIOnEvent behavoiur with any window


Recommended Posts


I wonder if it's possible to interrupt my script when a specific window closes. This is similar to GUISetOnEvent() but the window I'm using is not created by GUICreate(), it's just a window app. 

My script checks with some frecuency whether the window still exists. However, this is racy since this event is asynchronic w.r.t my script. When it happens, my script end up clicking any other window behind. 

Options to consider are

- Always use ControlClick to prevent clicks outside this window.

- With a timer, poll the existence of the window. Still racy but less probable to happen. (efficiency is not required) 

- Get interrupted whenever the window dissapears. (What I'm looking for) 

Actually, I use MouseClick with client coords. 

Thanks in advance 


Link to comment
Share on other sites

What you want is something along the lines of DllCallbackRegister + _WinAPI_SetWinEventHook. If set up correctly this will interrupt your script. I have a small example from a script that I've used it in previously, but it's not something that I use very often so I don't know a lot about it. I didn't change the code that was in my script to fit your needs, I'll leave that up to you. You will need to look up a couple resources to understand it more:




#include <WinAPI.au3>
#include <WinAPISys.au3>

Local $hEventProc = DllCallbackRegister('_EventProc', "none", "ptr;dword;hwnd;long;long;dword;dword")
Local $hEventHook = _WinAPI_SetWinEventHook($EVENT_MIN, $EVENT_MAX, DllCallbackGetPtr($hEventProc))


While 1

Func _EventProc($hEventHook, $iEvent, $hWnd, $iObjectID, $iChildID, $iThreadId, $iEventTime)
    #forceref $hEventHook, $iObjectID, $iChildID, $iThreadId, $iEventTime

    ; https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants

    Local $hCurrHnd
    Local $hTimer = TimerInit()
;~  ConsoleWrite('Event: ' & Hex($iEvent, 8) & @CRLF) ; Don't recommend that you enable this, you'll be spammed with logs

    Switch $iEvent
            $hCurrHnd = WinGetHandle('[ACTIVE]')
            While _WinAPI_GetParent($hWnd) <> 0
                $hWnd = _WinAPI_GetParent($hWnd)
            If $hCurrHnd <> $hWnd Then Return $iEvent
            ConsoleWrite(WinGetTitle($hWnd) & " - MoveEventEnd, " & Round(TimerDiff($hTimer), 2) & 'ms' & @CRLF)

;~          $hCurrHnd = __WinHandle()
;~          While _WinAPI_GetParent($hWnd) <> 0
;~              $hWnd = _WinAPI_GetParent($hWnd)
;~          WEnd
;~          If $hCurrHnd <> $hWnd Then Return False
;~          ConsoleWrite(WinGetTitle($hWnd) & " - Start" & @CRLF)

            $hCurrHnd = WinGetHandle('[ACTIVE]')
            While _WinAPI_GetParent($hWnd) <> 0
                $hWnd = _WinAPI_GetParent($hWnd)
            If $hCurrHnd <> $hWnd Then Return $iEvent
            ConsoleWrite("$EVENT_OBJECT_LOCATIONCHANGE - " & Round(TimerDiff($hTimer), 2) & 'ms' & @CRLF)

            $hCurrHnd = WinGetHandle('[ACTIVE]')
            While _WinAPI_GetParent($hWnd) <> 0
                $hWnd = _WinAPI_GetParent($hWnd)
            If $hCurrHnd <> $hWnd Then Return $iEvent
            ConsoleWrite("$EVENT_OBJECT_NAMECHANGE - " & Round(TimerDiff($hTimer), 2) & 'ms' & @CRLF)
EndFunc   ;==>_EventProc

Func __Exit()
EndFunc   ;==>__Exit


What you would want to do is likely have a function that wraps your MouseClick, and have a variable that you can toggle on/off if the window exists, or not. That way you can toggle the variable to false if the window is destroyed, and that can either exit your loop/function that's doing the clicks. Even if it's not at the point of checking that then having your MouseClick wrapped in a function that checks if the variable is true before clicking should keep you from clicking when the window doesn't exist. Something like 

Local $bClick = False
Func _MouseClick($sButton = 'main', $iX = Default, $iY = Default, $iClicks = 1, $iSpeed = 10)
    If $bClick = True Then Return MouseClick($sButton, $iX, $iY, $iClicks, $iSpeed)
EndFunc   ;==>_MouseClick


The other much more simple option is to use the _MouseClick function like above and replace the If check with If WinExists('YourWindow'), not very efficient but should take care of clicking when the window doesn't exist, for the most part (since it could still happen in a tiny time between the check and click function). ControlClick if it works for your window will be safer.

Edited by mistersquirrle

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

If you prefer using a hook then I would recommend using _WinAPI_RegisterShellHookWindow as it is more appropriate when a window closes.

Here an example :

#include <APISysConstants.au3>
#include <WinAPISysWin.au3>
#include <Constants.au3>

If WinExists("[CLASS:Notepad]") Then Exit MsgBox($MB_SYSTEMMODAL, "Error", "Close all instance of Notepad")
Global $hTarget = WinWait("[CLASS:Notepad]")


Global $hForm = GUICreate('')
GUIRegisterMsg(_WinAPI_RegisterWindowMessage('SHELLHOOK'), WM_SHELLHOOK)

While 1

Func WM_SHELLHOOK($hWnd, $iMsg, $wParam, $lParam)
  If $lParam <> $hTarget Then Return
  Switch $wParam
      ConsoleWrite("Notepad has been closed" & @CRLF)
EndFunc   ;==>WM_SHELLHOOK

Func OnAutoItExit()
EndFunc   ;==>OnAutoItExit

The script will stop when you close Notepad...

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

  • Create New...