SetTimer inside a method to launch another method.

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
XMCQCX
Posts: 225
Joined: 14 Oct 2020, 23:44

SetTimer inside a method to launch another method.

Post by XMCQCX » 24 Mar 2023, 19:11

Hi,

The timer is working if I point to the Instantiation of the Class. Not sure if it's the right term.

Code: Select all

SetTimer () => Monitor.WinStatusFinder(hWnd), -125
When pointing directly inside the class with "this." I get an error "Error: This value of type "Integer" has no method named "WinStatusFinder".

Code: Select all

SetTimer () => this.WinStatusFinder(hWnd), -125
I would be immensely grateful for any assistance you could offer. Thank you so much in advance for your help.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
Persistent

Monitor := Monitoring()

Monitor.Add({Window:[{WinTitle:"WordPad"}]})

Class Monitoring {

	Add(oEvent)
	{
        if oEvent.HasOwnProp("Window")
            SetTimer () => this.WindowMonitoring_Start(), -1
        /*
        Negative timer to wait until all windows are added before starting monitoring.

        The auto-execute thread has a priority of 0, so by default any timer with a negative priority will not fire 
        until after the auto-execute section finishes. - lexikos
        https://www.autohotkey.com/boards/viewtopic.php?style=17&p=460135
        */
    }
    
    WindowMonitoring_Start() 
    {
        this.HookProcAdr := CallbackCreate(this.CaptureWinEvent, "F")
        this.hWinEventHook := this.SetWinEventHook(0x1, 0x17, 0, this.HookProcAdr, 0, 0, 0x1)
    }

    SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
    {
        return DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", hmodWinEventProc, "Uint", lpfnWinEventProc, "Uint", idProcess, "Uint", idThread, "Uint", dwFlags)
    }

    CaptureWinEvent(Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime) 
    {
        SetTimer () => Monitor.WinStatusFinder(hWnd), -125  ; <= This is working.
        ; SetTimer () => this.WinStatusFinder(hWnd), -125  ; <= Why I get an error with this ? Error: This value of type "Integer" has no method named "WinStatusFinder". 
    }

    WinStatusFinder(hWnd)
    {
        ; Debug(hWnd)
    }
}

User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: SetTimer inside a method to launch another method.

Post by kczx3 » 24 Mar 2023, 21:21

I think in this case you must bind this to the callback that you pass to CallbackCreate. Or you could maybe pass an arrow function that then calls your method.

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: SetTimer inside a method to launch another method.

Post by swagfag » 25 Mar 2023, 03:46

ure doing

Code: Select all

CallbackCreate(this.CaptureWinEvent, "F")
ur Monitoring.Prototype.CaptureWinEvent is actually, under the hood, a function with the following parameter list: CaptureWinEvent(hidden_this_that_u_dont_see, Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime)
the WINEVENTPROC must be handler that will accept exactly 7 parameters:
image.png
image.png (7.64 KiB) Viewed 1039 times
so, hWinEventHook which is a HWINEVENTHOOK which is a void* to an opaque struct which is an Integer populates the first parameter - hidden_this_that_u_dont_see.
DWORD event populates the second parameter - Event
and so on

so in the end u have this, which inside the callback(once the hook invokes it for you) is actually HWINEVENTHOOK hWinEventHook and no longer an ahk object, that ure trying to invoke a method(.WinStatusFinder) on

XMCQCX
Posts: 225
Joined: 14 Oct 2020, 23:44

Re: SetTimer inside a method to launch another method.

Post by XMCQCX » 25 Mar 2023, 07:00

kczx3 wrote:
24 Mar 2023, 21:21
I think in this case you must bind this to the callback that you pass to CallbackCreate. Or you could maybe pass an arrow function that then calls your method.
swagfag wrote:
25 Mar 2023, 03:46
so in the end u have this, which inside the callback(once the hook invokes it for you) is actually HWINEVENTHOOK hWinEventHook and no longer an ahk object, that ure trying to invoke a method(.WinStatusFinder) on
Thanks a lot for the help, guys. It's very appreciated. I get it now why I get this error. CaptureWinEvent is not a method, but rather a function, and the 'this' parameter is already 'taken' for a hidden parameter (hWinEventHook). Is there a way to invoke a method inside the that function without referencing the instance 'Monitor'? I tried using the bind method, but didn't manage to make it work. Also, I get an error when trying to declare any type of object inside the CaptureWinEvent function. When exiting the script, I get the error message: 'Error: This value of type "Object" has no method named "Call".

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: SetTimer inside a method to launch another method.

Post by swagfag » 25 Mar 2023, 07:16

cant troubleshoot what u cant see
at the moment, i dont see anything

XMCQCX
Posts: 225
Joined: 14 Oct 2020, 23:44

Re: SetTimer inside a method to launch another method.

Post by XMCQCX » 25 Mar 2023, 07:31

swagfag wrote:
25 Mar 2023, 07:16
cant troubleshoot what u cant see
at the moment, i dont see anything
When exiting this script, I get the error message: 'Error: This value of type "Object" has no method named "Call". I don't understand why declaring an array or object in this function give this error.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
Persistent

Monitor := Monitoring()

Monitor.Add({Window:[{WinTitle:"WordPad"}]})

Class Monitoring {

	Add(oEvent)
	{
        if oEvent.HasOwnProp("Window")
            SetTimer () => this.WindowMonitoring_Start(), -1
    }
    
    WindowMonitoring_Start() 
    {
        this.HookProcAdr := CallbackCreate(this.CaptureWinEvent, "F")
        this.hWinEventHook := this.SetWinEventHook(0x1, 0x17, 0, this.HookProcAdr, 0, 0, 0x1)
    }

    SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
    {
        return DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", hmodWinEventProc, "Uint", lpfnWinEventProc, "Uint", idProcess, "Uint", idThread, "Uint", dwFlags)
    }

    CaptureWinEvent(Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime) 
    {
        Array := ["test1", "test2"]

        SetTimer () => Monitor.WinStatusFinder(hWnd), -125  ; <= This is working.
        ; SetTimer () => this.WinStatusFinder(hWnd), -125  ; <= Why I get an error with this ? Error: This value of type "Integer" has no method named "WinStatusFinder". 
    }

    WinStatusFinder(hWnd)
    {
        ; Debug(hWnd)
    }
}

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: SetTimer inside a method to launch another method.

Post by swagfag » 25 Mar 2023, 09:21

u havent unhooked the hook and ur ahk variables had already been deallocated/deleted as the script was exiting

XMCQCX
Posts: 225
Joined: 14 Oct 2020, 23:44

Re: SetTimer inside a method to launch another method.

Post by XMCQCX » 26 Mar 2023, 01:17

swagfag wrote:
25 Mar 2023, 09:21
u havent unhooked the hook and ur ahk variables had already been deallocated/deleted as the script was exiting
Thanks a lot for the help you provided! It seems that adding this code below has fixed the issue. I was having an issue about __Delete not being called when closing the script, so I ended up with this.

Code: Select all

    __New() 
    {
        OnExit ExitFunc(*) => this.OnExit()
    }

    OnExit()
    {
        DllCall("UnhookWinEvent", "Ptr", this.hWinEventHook)
        CallbackFree(this.HookProcAdr)
    }
I wasn't able to found a solution about the issue I mentioned in my first post. The fact that I need to point to the Instantiation of the Class (Monitor) instead of using "this." I did some testing with binding, but wasn't able to figure that out.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
Persistent

Monitor := Monitoring()

Monitor.Add({Window:[{WinTitle:"WordPad"}]})

Class Monitoring {

	Add(oEvent)
	{
        if oEvent.HasOwnProp("Window")
            SetTimer () => this.WindowMonitoring_Start(), -1
    }
    
    WindowMonitoring_Start() 
    {
        this.HookProcAdr := CallbackCreate(this.CaptureWinEvent, "F")
        this.hWinEventHook := this.SetWinEventHook(0x1, 0x17, 0, this.HookProcAdr, 0, 0, 0x1)
    }

    SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
    {
        return DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", hmodWinEventProc, "Uint", lpfnWinEventProc, "Uint", idProcess, "Uint", idThread, "Uint", dwFlags)
    }

    CaptureWinEvent(Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime) 
    {
        SetTimer () => Monitor.WinStatusFinder(hWnd), -125  ; <= This is working.
        ; SetTimer () => this.WinStatusFinder(hWnd), -125  ; <= Why I get an error with this ? Error: This value of type "Integer" has no method named "WinStatusFinder". 
    }

    WinStatusFinder(hWnd)
    {
        ; Debug(hWnd)
    }
}

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: SetTimer inside a method to launch another method.

Post by swagfag » 26 Mar 2023, 10:19

XMCQCX wrote:
26 Mar 2023, 01:17
having an issue about __Delete not being called when closing the script
because uve created circular reference(s) somewhere in ur code and now the object would never cease to exist and be garbage collected, until uve broken the circular references manually(or have exited the script, in which case its whatever, the OS will clean up)
I did some testing with binding, but wasn't able to figure that out.
its simple, u write:

Code: Select all

this.HookProcAdr := CallbackCreate(this.CaptureWinEvent.Bind(this), "F")
ur new problem now is managing the lifetime of ur instantiated object(which is an instance of the Class Monitoring, stored in the variable Monitor), because what uve just done was create a circular reference, binding this(incrementing its refcount by +1) and then storing the whole thing back into itself (this.HookProcAdr := ... bla bla this ....)

User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: SetTimer inside a method to launch another method.

Post by kczx3 » 26 Mar 2023, 14:02

That’s what I was getting at in my earlier reply. So in cases such as this would there need to be some kind of cleanup method to remove the circular reference? Or can you just unset this.hookProcAdr (after calling CallbackFree, of course) in __delete?

XMCQCX
Posts: 225
Joined: 14 Oct 2020, 23:44

Re: SetTimer inside a method to launch another method.

Post by XMCQCX » 26 May 2023, 10:04

swagfag wrote:
26 Mar 2023, 10:19
its simple, u write:

Code: Select all

this.HookProcAdr := CallbackCreate(this.CaptureWinEvent.Bind(this), "F")
Thanks, swagfag, for the help. This doesn't seem to work. I've tried solving this in numerous ways here and there over the last two months, but I haven't been able to bind "this" to the "CaptureWinEvent" function. I get missing parameters errors. I would greatly appreciate it if anyone could help me.

Code: Select all

#Requires AutoHotkey v2.0
#SingleInstance Force
Persistent

Monitor := Monitoring()

Monitor.WindowMonitoring_Start

Class Monitoring {

    WindowMonitoring_Start() 
    {
        this.HookProcAdr := CallbackCreate(this.CaptureWinEvent.Bind(this), "F")
        this.hWinEventHook := this.SetWinEventHook(0x1, 0x17, 0, this.HookProcAdr, 0, 0, 0x1)
    }

    SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
    {
        return DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", hmodWinEventProc, "Uint", lpfnWinEventProc, "Uint", idProcess, "Uint", idThread, "Uint", dwFlags)
    }

    CaptureWinEvent(Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime)
    {
        SetTimer () => this.WinStatusFinder(hWnd), -25 
    }

    WinStatusFinder(hWnd)
    {
        ; Debug(hWnd)
    }
}

ntepa
Posts: 407
Joined: 19 Oct 2022, 20:52

Re: SetTimer inside a method to launch another method.

Post by ntepa » 26 May 2023, 18:02

The 3rd parameter (ParamCount) was omitted in CallbackCreate. If there's no ParamCount, the Function's MinParams property is used. Bound funcs have 0 MinParams.

Code: Select all

Monitor := Monitoring()

Monitor.WindowMonitoring_Start

Class Monitoring {

    WindowMonitoring_Start()
    {
        this.HookProcAdr := CallbackCreate(this.CaptureWinEvent.Bind(this), "F", 7)
        this.hWinEventHook := this.SetWinEventHook(0x1, 0x17, 0, this.HookProcAdr, 0, 0, 0x1)
    }

    SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
    {
        return DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", hmodWinEventProc, "Uint", lpfnWinEventProc, "Uint", idProcess, "Uint", idThread, "Uint", dwFlags)
    }

    CaptureWinEvent(hWinEventHook, Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime)
    {
        SetTimer () => this.WinStatusFinder(hWnd), -25
    }

    WinStatusFinder(hWnd)
    {
        ; Debug(hWnd)
    }
}

XMCQCX
Posts: 225
Joined: 14 Oct 2020, 23:44

Re: SetTimer inside a method to launch another method.

Post by XMCQCX » 27 May 2023, 07:33

ntepa wrote:
26 May 2023, 18:02
The 3rd parameter (ParamCount) was omitted in CallbackCreate. If there's no ParamCount, the Function's MinParams property is used. Bound funcs have 0 MinParams.
Awesome, thanks a lot, ntepa, for the solution and explanation. It's really appreciated !

Post Reply

Return to “Ask for Help (v2)”