Jump to content

Recommended Posts

Posted

Hello Forum,

I'm trying to create a working example for CreateTimerQueueTimer, but in my example the script exits without error before reaching the timeout of 5 seconds. I appreciate any input on this.

CreateTimerQueueTimer > https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx

; https://www.codeproject.com/KB/system/timers_intro.aspx
; > If you want a high-precision, low-overhead, non-blocking timer that will work on Windows 2000 and later, use the queue timer.

; Example by trancexx > https://www.autoitscript.com/forum/topic/87702-myip/#comment-631759

#include <WinAPIError.au3>
#include <WinAPIHObj.au3>

Local $hCallback = DllCallbackRegister("_CallBackFunction", "none", "lparam;bool")
Func _CallBackFunction($lParam, $TimerOrWaitFired)
    ConsoleWrite("_CallBackFunction fired = " & TimerInit() & @TAB & $lParam & @TAB & $TimerOrWaitFired & @CRLF)
EndFunc   ;==>_CallBackFunction

Local Const $WT_EXECUTELONGFUNCTION = 0x00000010 ; The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
Local Const $WT_EXECUTEINTIMERTHREAD = 0x00000020 ; The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
Local Const $WT_EXECUTEINPERSISTENTTHREAD = 0x00000080 ; The callback function is queued to a thread that never terminates.

Local $a_h_CreateTimerQueue = DllCall("kernel32.dll", "handle", "CreateTimerQueue")
ConsoleWrite("CreateTimerQueue = " & $a_h_CreateTimerQueue[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

Local $i_TimerQueue_Start_after = 100
Local $i_TimerQueue_Repeat_after = 100

; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx
Local $a_hCall = DllCall("kernel32.dll", "bool", "CreateTimerQueueTimer", _
        "handle*", 0, _
        "handle", $a_h_CreateTimerQueue[0], _
        "ptr", DllCallbackGetPtr($hCallback), _
        "ptr", 678, _
        "dword", $i_TimerQueue_Start_after, _
        "dword", $i_TimerQueue_Repeat_after, _
        "ulong", BitOR($WT_EXECUTELONGFUNCTION, $WT_EXECUTEINTIMERTHREAD))
Local $phNewTimer = $a_hCall[1]
ConsoleWrite("CreateTimerQueueTimer = " & $a_hCall[0] & @TAB & $phNewTimer & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

Local $timer = TimerInit()
While 1
    ConsoleWrite("+ " & TimerDiff($timer) & @CRLF)
    If TimerDiff($timer) > 5000 Then
        ConsoleWrite("! fire Exitloop event" & @CRLF)
        ExitLoop
    EndIf
    Sleep(10)
WEnd

Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueTimer", _
        "handle", $a_h_CreateTimerQueue[0], _
        "handle", $phNewTimer, _
        "handle", 0)
ConsoleWrite("DeleteTimerQueueTimer = " & $a_hCall[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

ConsoleWrite("_WinAPI_CloseHandle($phNewTimer) = " & _WinAPI_CloseHandle($phNewTimer) & @CRLF)

Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueEx", "handle", $a_h_CreateTimerQueue[0], "handle", 0)
ConsoleWrite("DeleteTimerQueueEx = " & $a_hCall[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

DllCallbackFree($hCallback)

Exit

#cs
    ; https://www.autohotkey.com/boards/viewtopic.php?t=24997

    #SingleInstance force

    qt := new QueueTimer(5, Func("Test")).Start()
    Sleep 10000
    qt.Stop()
    SoundBeep
    Sleep 2000
    qt.Start()
    Sleep 2000
    return
    ; Destructor auto-fires, de-registering timer on exit

    Test(){
        ToolTip % A_TickCount
    }

    ; Implements CreateTimerQueueTimer
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx
    class QueueTimer {
        TimerFunc := 0              ; Pointer to the function object to be called on timer tick
        Period := 0                 ; Time (in ms) that the timer fires at
        Delay := 0                  ; Amount of time before first fire
        Running := 0                ; Whether or not the timer is running
        RegisteredCallback := 0     ; Pointer to the callback registered with RegisterCallback
        hTimer := 0                 ; handle to the timer-queue timer
        hQueue := 0                 ; handle to the timer queue
        Freq := 0                   ; Number of ticks per second, or 0 if high-resolution performance counters not supported

        __New(period, TimerFunc := 0, delay := 0){
            DllCall("QueryPerformanceFrequency", "Int64*", freq)
            this.freq := freq
            if (this.freq == 0 || TimerFunc == 0 || period == 0)
                return 0
            this.TimerFunc := TimerFunc
            this.period := period
            this.delay := delay
        }

        __Delete(){
            this.Stop()
        }

        Start(){
            if (this.Running){
                this.Stop()
            }
            this.Running := 1
            this.hQueue := DllCall("kernel32.dll\CreateTimerQueue")
            hTimer := 0
            this.RegisteredCallback := RegisterCallback(this.TimerFunc,"F")
            DllCall("kernel32.dll\CreateTimerQueueTimer", "Ptr", &hTimer, "Ptr", this.hQueue, "UInt"
                , this.RegisteredCallback, "UInt", 0, "UInt", this.delay, "UInt", this.period, "Int", 0x00000000)
            this.hTimer := hTimer
            return this ; Allow chaining
        }

        Stop(){
            if (this.Running){
                DllCall("kernel32.dll\DeleteTimerQueueTimer", "Ptr", this.hQueue, "Ptr", this.hTimer, "Ptr", 0)
                DllCall("kernel32.dll\DeleteTimerQueueEx", "Ptr", this.hQueue, "Ptr", 0)
                DllCall("GlobalFree", "Ptr", this.RegisteredCallback, "Ptr")
                this.Running := 0
            }
            return this
        }
    }

#ce

Best Regards

Posted (edited)
Posted (edited)

Same issue on my Win11 system when running it as x86. With x64 it works.

Edit: confirm 

2 minutes ago, Nine said:

Even using only $WT_EXECUTEINTIMERTHREAD, it is still happening...

 

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted
Posted (edited)
12 minutes ago, UEZ said:

With x64 it works.

On my Win10, it breaks less often, but it is still happening....

Even crashing (really looks like a thread creation issue) :

!>14:53:14 AutoIt3 ended. rc:-1073741819

 

Edited by Nine
Posted

I think I have found the source of the issue.  As described in the function :

 

WT_EXECUTEINTIMERTHREAD
0x00000020
The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.

The callback function is queued as an APC. It should not perform alertable wait operations.

APC : Asynchronous Procedure Call.  Which means it is not executed immediately.  I thought that using Volatile would help, but it does not.

 

Posted
Combining WT_EXECUTELONGFUNCTION and WT_EXECUTEINTIMERTHREAD is technically possible but not recommended. WT_EXECUTEINTIMERTHREAD is for short tasks, and long tasks (allowed by WT_EXECUTELONGFUNCTION) may disrupt other timers via APCs. Use WT_EXECUTEDEFAULT for better stability.
Best regards,
Posted (edited)

No issues with Freebasic:

#include "windows.bi"
#include "win/winbase.bi"


Sub WoTCallback(LPARAM As LPARAM, TimerOrWaitFired As BOOL)
    ? LPARAM, TimerOrWaitFired
End Sub

Dim As PHANDLE phNewTimer
Dim As HANDLE hTQ = CreateTimerQueue()
CreateTimerQueueTimer(@phNewTimer, hTQ, Cast(WAITORTIMERCALLBACK, @WoTCallback), 0, 100, 100, WT_EXECUTEDEFAULT)

Dim As Double t = Timer
While 1
    If Timer - t > 5 Then
        ? !"Exit loop"
        DeleteTimerQueueTimer(hTQ, phNewTimer, 0)
        CloseHandle(phNewTimer)
        Exit While
    End If
    Sleep(10)
Wend

Sleep

Seems to be an issue with Autoit.

 

Edit:

Local $a_hCall = DllCall("kernel32.dll", "bool", "CreateTimerQueueTimer", _
        "struct*", $tBuffer, _
        "handle", $a_h_CreateTimerQueue[0], _
        "ptr", DllCallbackGetPtr($hCallback), _
        "ptr", Null, _
        "dword", 5000, _
        "dword", $i_TimerQueue_Repeat_after, _
        "ulong", 0)

This seems to be stable.

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted

Thanks for all the feedback 🙂👍!

UEZ, it's not stable for me. I think you've set the initial delay too high with 5000, this way the callback is called just before the timeout. When you reduce it to 100, I think it will fail for you too again.

Posted (edited)

You are right.

 

What about:

Local $tBuffer = DllStructCreate("handle buffer")

; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx
Local $a_hCall = DllCall("kernel32.dll", "bool", "CreateTimerQueueTimer", _
        "struct*", $tBuffer, _
        "handle", 0, _
        "ptr", DllCallbackGetPtr($hCallback), _
        "ptr", Null, _
        "dword", 100, _
        "dword", 150, _
        "ulong", 0)

...
Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueTimer", _
        "handle", 0, _
        "handle", $tBuffer.buffer, _
        "handle", 0)

 

Forget: it still doesn't seem to be stable.

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted (edited)
2 hours ago, Numeric1 said:

Use WT_EXECUTEDEFAULT for better stability.

Does not work, it make it worst in fact, because it will create threads.

ps. If anybody wants another proof, just replace the while loop by a MsgBox, it will run forever without any issue.  Attempt after attempt.

Edited by Nine
Posted (edited)

Thanks for pointing that out 🙂! I'll test @Danyfirex's function from here later the day.

Edit: No luck yet, the callback fires one time and on the second call the parameters are gone and the process crashes.

#include <WinAPI.au3>
#include <Memory.au3>

Global $dll_kernel32 = DllOpen("kernel32.dll")

Func _CallBackFunction($arg1, $arg2)
    ; MsgBox(0, "_MyFn - Child Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "handle", "GetCurrentThread")[0] & @CRLF & "$arg1:  " & Int($arg1) & @CRLF & "$arg2:  " & Int($arg2))
    ConsoleWrite("_CallBackFunction" & @tab & TimerInit() & @crlf)
EndFunc   ;==>_CallBackFunction

; https://www.autoitscript.com/forum/topic/211447-registersynccallback/#findComment-1529893
; by Danyfirex
Local $hCallback = _RegisterSyncCallback("_CallBackFunction", 2)
ConsoleWrite("$hCallback = " & $hCallback & @crlf)

Local $tParameters = DllStructCreate("int Value1;int Value2")
$tParameters.Value1 = 10
$tParameters.Value2 = 30
;~ DllCallAddress("int", $pCB, "ptr", DllStructGetPtr($tParameters))
ConsoleWrite("DllStructGetPtr($tParameters): " & DllStructGetPtr($tParameters) & @CRLF)

DllCall($dll_kernel32, "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $hCallback, "ptr", DllStructGetPtr($tParameters), "long", 0, "int*", 0)

Local Const $WT_EXECUTELONGFUNCTION = 0x00000010 ; The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
Local Const $WT_EXECUTEINTIMERTHREAD = 0x00000020 ; The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
Local Const $WT_EXECUTEINPERSISTENTTHREAD = 0x00000080 ; The callback function is queued to a thread that never terminates.

Local $i_TimerQueue_Start_after = 100
Local $i_TimerQueue_Repeat_after = 100

Local $a_hCall = DllCall($dll_kernel32, "bool", "CreateTimerQueueTimer", _
        "handle*", 0, _
        "handle", Null, _
        "ptr", $hCallback, _
        "ptr", Null, _
        "dword", 100, _
        "dword", $i_TimerQueue_Repeat_after, _
        "ulong", BitOR($WT_EXECUTELONGFUNCTION, $WT_EXECUTEINTIMERTHREAD))

Local $phNewTimer = $a_hCall[1]
ConsoleWrite("CreateTimerQueueTimer = " & $a_hCall[0] & @TAB & $phNewTimer & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

Local $timer = TimerInit()
ConsoleWrite("! Main loop - START" & @crlf)
While 1
    ConsoleWrite("+ " & TimerDiff($timer) & @CRLF)
    If TimerDiff($timer) > 5000 Then
        ConsoleWrite("! fire Exitloop event" & @CRLF)
        ExitLoop
    EndIf
    Sleep(10)
WEnd
ConsoleWrite("! Main loop - END" & @crlf)

Local $a_hCall = DllCall($dll_kernel32, "bool", "DeleteTimerQueueTimer", _
        "handle", 0, _
        "handle", $phNewTimer, _
        "handle", 0)
ConsoleWrite("DeleteTimerQueueTimer = " & $a_hCall[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

ConsoleWrite("_WinAPI_CloseHandle($phNewTimer) = " & _WinAPI_CloseHandle($phNewTimer) & @CRLF)

Exit



Func _RegisterSyncCallback($Function, $iParamCount = 0, $iOptions = 0)
    Local Static $hGUI
    Local Static $pSendMessage
    Local Static $iMsg

    If Not $hGUI Then
        $hGUI = GUICreate("RegisterSyncCallback_Msg", 300, 200)
        $iMsg = 0x8000
        GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg)
        $pSendMessage = DllCall($dll_kernel32, "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("user32.dll"), "str", "SendMessageW")[0]
        ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF)
        ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF)
        ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    EndIf

    If @AutoItX64 Then Return MsgBox(0, "Error", "This is a x86 Sample :(")  ; add x64 code yourself

    Local $pRemoteCode = _MemVirtualAlloc(0, 96, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(")
    Local $tCodeBuffer = DllStructCreate("byte[96]", $pRemoteCode)
    Local $hHandle = DllCallbackRegister($Function, "int", "int;int") ; hardcode one parameter

    ; Local $tExtradata = DllStructCreate("wchar[128]")
    ; DllStructSetData($tExtradata,1,"TestData")

    Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 30) & _
            "8D4424085068" & SwapEndian($iMsg) & "68" & SwapEndian($hGUI) & _
            "B8" & SwapEndian($pSendMessage) & "FFD0C2" & StringLeft(SwapEndian($iParamCount * 4), 4) & _
            SwapEndian(DllCallbackGetPtr($hHandle)) & SwapEndian($iParamCount)
            ; & SwapEndian(DllStructGetPtr($tExtradata)) ;<<== added this line and it worked > by jugador

    DllStructSetData($tCodeBuffer, 1, $sOPCode)
    ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF)

    Return $pRemoteCode

EndFunc   ;==>_RegisterSyncCallback

Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam)
    ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF)
    ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF)
    ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
    ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF)
    ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF)
    Local $tStruct = DllStructCreate("ptr pFunction", $lParam)
    Local $tNParameters = DllStructCreate("ptr NParameters", $lParam + 4)
    Local $tStructParameters = DllStructCreate("ptr", $wParam)

    ConsoleWrite("$tStruct.pFunction: " & $tStruct.pFunction & @CRLF)
    ConsoleWrite("$tNParameters.NParameters: " & $tNParameters.NParameters & @CRLF)
    Local $aValues[$tNParameters.NParameters]
    For $i = 0 To $tNParameters.NParameters - 1
        $aValues[$i] = DllStructGetData(DllStructCreate("int", DllStructGetData($tStructParameters, 1) + ($i * 4)), 1)
        ConsoleWrite($aValues[$i] & @CRLF)
    Next
    DllCallAddress("int", $tStruct.pFunction, "int", $aValues[0], "int", $aValues[1])
    Return 1
EndFunc   ;==>RegisterSyncCallback_Msg

Func SwapEndian($hex)
    Return Hex(BitOR(BitOR(BitOR(BitShift($hex, 24), _
            BitAND(BitShift($hex, -8), 0x00FF0000)), _
            BitAND(BitShift($hex, 8), 0x0000FF00)), _
            BitShift($hex, -24)), 8)
EndFunc   ;==>SwapEndian

 

Edited by KaFu
Posted (edited)

Log....

CreateTimerQueue = 0x009642F0

0   The operation completed successfully.

> __RegisterSyncCallback()
CreateTimerQueueTimer = 1   0x028E5658

0   The operation completed successfully.

+ 0.0034995748016616
+ 0.60787614304862
+ 16.2135300560982
+ 31.8118348620643
+ 47.4377863089635
+ 63.0290919653262
+ 78.6399952405783
+ 94.2295511095402
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13655139949   678 1
+ 110.111671431921
+ 125.468855534053
+ 141.079408851825
+ 156.657066166461
+ 172.285117358241
+ 187.850876118552
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13655407793   678 1
+ 203.846032707026
+ 219.01843925963
+ 234.60484551127
+ 250.248994747138
+ 265.851849000346
+ 281.46660180788
+ 297.058957336684
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13655719562   678 1
+ 312.97432361968
+ 328.367203384789
+ 343.857721286864
+ 359.56136329436
+ 375.0630798358
+ 390.660334769326
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13655986965   678 1
+ 406.562402668076
+ 421.866743190702
+ 437.448599995101
+ 453.05320403571
+ 468.631211307826
+ 484.277460288575
+ 499.850218198489
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13656298991   678 1
+ 515.744237075195
+ 531.138516670225
+ 546.67242929984
+ 562.260235381401
+ 577.852240952724
+ 593.410300648471
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13656566742   678 1
+ 609.445002432205
+ 624.68390090604
+ 640.281855754526
+ 655.895908647099
+ 671.437520341279
+ 687.063121830698
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13656833926   678 1
+ 702.952941217642
+ 718.183090754473
+ 734.022516264274
+ 749.439893052994
+ 765.041347476282
+ 780.669398668062
+ 796.236907215773
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13657145936   678 1
+ 812.146674179087
+ 827.440865934789
+ 843.030071846271
+ 858.762760324621
+ 874.218632436159
+ 889.894977760202
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13657413684   678 1
+ 905.861437835303
+ 921.077939030408
+ 936.661895579687
+ 952.244802256526
+ 967.851855999496
+ 983.438612208617
+ 999.077512082282
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13657725433   678 1
+ 1014.93828499837
+ 1030.22127811471
+ 1045.83218138996
+ 1061.44273470773
+ 1077.06343679243
+ 1092.58475095276
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13657992794   678 1
+ 1108.47527025466
+ 1123.87199955205
+ 1139.44475746197
+ 1155.02871401125
+ 1170.65466545815
+ 1186.23442251766
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13658260349   678 1
+ 1202.14803901326
+ 1217.45272949337
+ 1233.03493625525
+ 1248.60629433524
+ 1264.26549174275
+ 1279.83999944007
+ 1295.48414867594
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13658572655   678 1
+ 1311.45165862348
+ 1326.66361037134
+ 1342.20522206552
+ 1357.90326475333
+ 1373.45362538452
+ 1389.02708320939
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13658839873   678 1
+ 1404.95959740892
+ 1420.22124311896
+ 1435.81989788241
+ 1451.45634805371
+ 1467.04835362503
+ 1482.62006166251
+ 1498.2285152354
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13659152175   678 1
+ 1514.52953466154
+ 1529.55075958271
+ 1545.04792667691
+ 1560.71727285135
+ 1576.321176977
+ 1591.82394339088
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13659419428   678 1
+ 1607.77360550693
+ 1623.0842452642
+ 1638.63320606546
+ 1654.23955989347
+ 1669.82526623015
+ 1685.57720236991
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13659686876   678 1
+ 1701.34418668132
+ 1716.73951614879
+ 1732.25978043668
+ 1747.85598549776
+ 1763.4864863919
+ 1779.01584957428
+ 1794.62395318969
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13659999413   678 1
+ 1810.75979268519
+ 1825.92694987559
+ 1841.51405604219
+ 1857.01752237103
+ 1872.61197764472
+ 1888.22498066485
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13660266242   678 1
+ 1904.1270485636
+ 1919.4142411697
+ 1935.11473355987
+ 1950.64724635957
+ 1966.34458913242
+ 1981.88655078408
+ 1997.48660537745
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13660578286   678 1
+ 2013.32323122741
+ 2028.62827166499
+ 2044.21607774655
+ 2059.80073421079
+ 2075.43298489234
+ 2091.0312896983
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13660845977   678 1
+ 2107.01314790253
+ 2122.26569471809
+ 2137.8262041162
+ 2153.42450892217
+ 2169.02141389821
+ 2184.65331462227
+ 2200.20367525346
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13661157800   678 1
+ 2216.1340897081
+ 2231.41078358979
+ 2247.1319234713
+ 2262.61404239385
+ 2278.46186688317
+ 2293.80995209082
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13661425281   678 1
+ 2309.73581709822
+ 2325.00201225551
+ 2340.59611757172
+ 2356.21017046429
+ 2371.8130247175
+ 2387.50581804311
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13661692445   678 1
+ 2403.1989613262
+ 2418.61388841256
+ 2434.2107933886
+ 2449.80559861977
+ 2465.41125253282
+ 2481.04595291672
+ 2496.60751218727
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13662004730   678 1
+ 2512.52357838523
+ 2527.88391210468
+ 2543.39997690281
+ 2559.00598077334
+ 2574.71417222807
+ 2590.20678987503
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13662272102   678 1
+ 2606.07806151553
+ 2621.38975114524
+ 2637.01780233702
+ 2652.59196007685
+ 2668.20811271431
+ 2683.78961956122
+ 2699.42012045536
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13662584438   678 1
+ 2715.40057882967
+ 2730.6996699901
+ 2746.21888440554
+ 2761.79374206034
+ 2777.40184567575
+ 2793.00959933368
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13662851660   678 1
+ 2808.90746774267
+ 2824.19850988105
+ 2839.7670683012
+ 2855.39441957802
+ 2871.00602276823
+ 2886.59487872224
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13663119141   678 1
+ 2902.51479445247
+ 2917.77888986488
+ 2933.42303910075
+ 2948.99229743586
+ 2964.57800377254
+ 2980.24420032966
+ 2995.80156011045
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13663431122   678 1
+ 3011.68228060291
+ 3026.98767099797
+ 3042.79350058968
+ 3058.19967873903
+ 3073.80953214185
+ 3089.3903390738
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13663698598   678 1
+ 3105.30290569696
+ 3120.73533065733
+ 3136.21010047279
+ 3151.81610434332
+ 3167.40181068
+ 3183.00676467809
+ 3198.60052003682
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13664010694   678 1
+ 3214.52638504422
+ 3229.81147790543
+ 3245.40768296652
+ 3261.00423798509
+ 3276.65713615796
+ 3292.32438258752
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13664277909   678 1
+ 3308.02382510525
+ 3323.493695516
+ 3338.99191248263
+ 3354.61261456733
+ 3370.19797094653
+ 3385.77877787849
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13664545732   678 1
+ 3401.76203591264
+ 3417.10067226832
+ 3432.52959765389
+ 3448.29728188025
+ 3463.7706518658
+ 3479.43124910323
+ 3494.99455816118
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13664857692   678 1
+ 3510.93302163787
+ 3526.17506972903
+ 3541.93855446563
+ 3557.35978078663
+ 3573.08092066814
+ 3588.61798291508
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13665124958   678 1
+ 3604.45250902015
+ 3619.76384869238
+ 3635.38210107472
+ 3650.9783061358
+ 3666.57941060161
+ 3682.19206366426
+ 3697.7627218293
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13665437013   678 1
+ 3713.6549909186
+ 3728.98697808216
+ 3744.58423301569
+ 3760.1993357807
+ 3775.78329232998
+ 3791.37284819894
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13665704545   678 1
+ 3807.52898522829
+ 3822.57225747072
+ 3838.18561044833
+ 3853.83745874876
+ 3869.43121410749
+ 3885.04911653234
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13665971999   678 1
+ 3900.89414136182
+ 3916.17188511596
+ 3931.7582913676
+ 3947.38109319718
+ 3962.98149774802
+ 3978.63649566578
+ 3994.1543102513
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13666284135   678 1
+ 4010.11587092168
+ 4025.39431459078
+ 4041.00101837627
+ 4056.57937560586
+ 4072.19517828584
+ 4087.75428785403
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13666551606   678 1
+ 4103.71829822677
+ 4118.91415193054
+ 4134.65663921833
+ 4150.1772534637
+ 4165.75631060826
+ 4181.41795771814
+ 4197.0057637997
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13666863498   678 1
+ 4212.889283952
+ 4228.16702770613
+ 4243.75098425541
+ 4259.49662116053
+ 4274.99238842481
+ 4290.59034327329
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13667130945   678 1
+ 4306.47491329804
+ 4321.75335696713
+ 4337.37510892427
+ 4352.96046530347
+ 4368.5699687488
+ 4384.31595561139
+ 4399.7935250867
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13667443029   678 1
+ 4415.66514668468
+ 4430.96528771754
+ 4446.55799320383
+ 4462.16784660664
+ 4477.77280060473
+ 4493.37495494297
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13667710589   678 1
+ 4509.32601688895
+ 4524.54076829665
+ 4540.36969508205
+ 4555.7776230188
+ 4571.72693517738
+ 4586.97563246066
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13667977866   678 1
+ 4602.84900384603
+ 4618.15649398598
+ 4633.73905070534
+ 4649.36325236484
+ 4664.9563078086
+ 4680.56056189173
+ 4696.15851674022
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13668289795   678 1
+ 4712.01194054922
+ 4727.41706882614
+ 4742.9236847723
+ 4758.60842907587
+ 4774.14269166296
+ 4789.73784685161
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13668557410   678 1
+ 4805.6833094779
+ 4820.96315297691
+ 4836.57650595453
+ 4852.21330608331
+ 4867.74091947828
+ 4883.3612716055
+ 4898.9553769217
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13668869426   678 1
+ 4914.86969333226
+ 4930.14533734151
+ 4945.75064129708
+ 4961.34334678337
+ 4976.99414521136
+ 4992.52980762837
> __RegisterSyncCallback_Msg()
_CallBackFunction fired = 13669136857   678 1
+ 5008.44587382633
! fire Exitloop event
DeleteTimerQueueTimer = 1

0   The operation completed successfully.

_WinAPI_CloseHandle($phNewTimer) = 0
DeleteTimerQueueEx = 1

6   The handle is invalid.

+> AutoIt3.exe ended.rc:0
+> AutoIt3Wrapper Finished.
>Exit code: 0    Time: 6.258

 

Edited by jugador
Posted (edited)

Here :

#include <Memory.au3>
#include <WindowsConstants.au3>
#include <WinAPIError.au3>
#include <WinAPIHObj.au3>
#include <WinAPISys.au3>

Global Const $WT_EXECUTELONGFUNCTION = 0x00000010 ; The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
Global Const $WT_EXECUTEINTIMERTHREAD = 0x00000020 ; The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
Global Const $WT_EXECUTEINPERSISTENTTHREAD = 0x00000080 ; The callback function is queued to a thread that never terminates.

Local $a_h_CreateTimerQueue = DllCall("kernel32.dll", "handle", "CreateTimerQueue")
ConsoleWrite("CreateTimerQueue = " & $a_h_CreateTimerQueue[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

Local $i_TimerQueue_Start_after = 100
Local $i_TimerQueue_Repeat_after = 100

Local $pCallback = _RegisterSyncCallback(_CallBackFunction)

; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx
Local $a_hCall = DllCall("kernel32.dll", "bool", "CreateTimerQueueTimer", _
    "handle*", 0, _
    "handle", $a_h_CreateTimerQueue[0], _
    "ptr", $pCallback, _
    "ptr", 678, _
    "dword", $i_TimerQueue_Start_after, _
    "dword", $i_TimerQueue_Repeat_after, _
    "ulong", $WT_EXECUTEINTIMERTHREAD)
Local $phNewTimer = $a_hCall[1]
ConsoleWrite("CreateTimerQueueTimer = " & $a_hCall[0] & @TAB & $phNewTimer & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

Local $timer = TimerInit()
While Sleep(10)
  ConsoleWrite("+ " & TimerDiff($timer) & @CRLF)
  If TimerDiff($timer) > 5000 Then
    ConsoleWrite("! fire Exitloop event" & @CRLF)
    ExitLoop
  EndIf
WEnd

Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueTimer", _
    "handle", $a_h_CreateTimerQueue[0], _
    "handle", $phNewTimer, _
    "handle", 0)
ConsoleWrite("DeleteTimerQueueTimer = " & $a_hCall[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

ConsoleWrite("_WinAPI_CloseHandle($phNewTimer) = " & _WinAPI_CloseHandle($phNewTimer) & @CRLF)

Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueEx", "handle", $a_h_CreateTimerQueue[0], "handle", 0)
ConsoleWrite("DeleteTimerQueueEx = " & $a_hCall[0] & @CRLF & @CRLF)
ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF)

Func _CallBackFunction($lParam, $TimerOrWaitFired)
  ConsoleWrite("_CallBackFunction fired = " & $lParam & @TAB & $TimerOrWaitFired & @CRLF)
EndFunc   ;==>_CallBackFunction

Func _RegisterSyncCallback($Function)
  Local Static $hGUI
  Local Static $pSendMessage
  Local Static $iMsg

  If Not $hGUI Then
    $hGUI = GUICreate("RegisterSyncCallback_Msg", 300, 200)
    $iMsg = $WM_APP
    GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg)
    $pSendMessage = _WinAPI_GetProcAddress(_WinAPI_GetModuleHandle("user32.dll"), "SendMessageW")
    ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF)
    ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF)
    ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
  EndIf

  If @AutoItX64 Then Return MsgBox(0, "Error", "This is a x86 Sample :(")    ;add x64 code yourself

  Local $pRemoteCode = _MemVirtualAlloc(0, 96, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
  If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(")
  Local $tCodeBuffer = DllStructCreate("byte[96]", $pRemoteCode)
  Local $hHandle = DllCallbackRegister($Function, "none", "int;int")

  Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 29) & _
      "FF74240868" & SwapEndian($iMsg) & "68" & SwapEndian($hGUI) & _
      "B8" & SwapEndian($pSendMessage) & "FFD0C20800" & _
      SwapEndian(DllCallbackGetPtr($hHandle))

  DllStructSetData($tCodeBuffer, 1, $sOPCode)
  ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF)

  Return $pRemoteCode
EndFunc   ;==>_RegisterSyncCallback

Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam)
  ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF)
  ;ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF)
  ;ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF)
  ;ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF)
  ;ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF)
  Local $tStruct = DllStructCreate("ptr pFunction", $lParam)
  DllCallAddress("none", $tStruct.pFunction, "int", $wParam, "int", 1)
EndFunc   ;==>RegisterSyncCallback_Msg

Func SwapEndian($uInt)
  Local $iLen = @AutoItX64 ? 16 : 8, $sHex = Hex($uInt, $iLen), $sRes
  For $i = $iLen - 2 To 0 Step -2
    $sRes &= StringMid($sHex, $i + 1, 2)
  Next
  Return $sRes
EndFunc

What the ASM code is doing :

0:  68 1d 00 c3 03          push   0x3c3001d  ; hHandle for lParam
5:  ff 74 24 08             push   DWORD PTR [esp+0x8] ; Parameter for wParam
9:  68 00 80 00 00          push   0x8000   ; iMsg
e:  68 4e 07 06 00          push   0x6074e  ; hGUI
13: b8 60 5d 4e 76          mov    eax,0x764e5d60
18: ff d0                   call   eax      ; SendMessageW
1a: c2 08 00                ret    0x8

 

Edited by Nine
better code
Posted (edited)

Great 🙂, works for me too 👍!

The team over at the AHK forum also posted the opcode for x64 mode.

https://www.autohotkey.com/boards/viewtopic.php?p=538378#p538378

p := pcb
    if A_PtrSize = 8 {
        /*
        48 89 4c 24 08  ; mov [rsp+8], rcx
        48 89 54'24 10  ; mov [rsp+16], rdx
        4c 89 44 24 18  ; mov [rsp+24], r8
        4c'89 4c 24 20  ; mov [rsp+32], r9
        48 83 ec 28'    ; sub rsp, 40
        4c 8d 44 24 30  ; lea r8, [rsp+48]  (arg 3, &params)
        49 b9 ..        ; mov r9, .. (arg 4, operand to follow)
        */
        p := NumPut('Ptr'  , 0x54894808244c8948,
                    'Ptr'  , 0x4c182444894c1024,
                    'Ptr'  , 0x28ec834820244c89,
                    'Ptr'  , 0x00b9493024448d4c, p) - 1
        lParamPtr := p, p += 8

        p := NumPut('Char' , 0xba,        ; mov edx, nmsg
                    'Int'  , msg, 
                    'Char' , 0xb9,        ; mov ecx, hwnd
                    'Int'  , wnd.hwnd, 
                    'Short', 0xb848,      ; mov rax, SendMessageW
                    'Ptr'  , SendMessageW,
                            /*
                            ff d0         ; call rax
                            48 83 c4 28   ; add rsp, 40
                            c3            ; ret
                            */
                    'Ptr'  , 0x00c328c48348d0ff, p)
    } else {
        p := NumPut('Char' , 0x68, p)     ; push ... (lParam data)
        lParamPtr := p, p += 4
        p := NumPut('Int'  , 0x0824448d,  ; lea eax, [esp+8]
                    'Char' , 0x50,        ; push eax
                    'Char' , 0x68,        ; push nmsg
                    'Int'  , msg, 
                    'Char' , 0x68,        ; push hwnd
                    'Int'  , wnd.hwnd, 
                    'Char' , 0xb8,        ; mov eax, &SendMessageW
                    'Int'  , SendMessageW,
                    'Short', 0xd0ff,      ; call eax
                    'Char' , 0xc2,        ; ret argsize
                    'Short', InStr(Options, 'C') ? 0 : ParamCount * 4, p)
    }

I'll give it a try, although I have to admit I'm not good in this assembly stuff.

Edit: Found this for SwapEndian under x64.

uint64_t swapLong(void *X) {
  uint64_t x = (uint64_t) X;
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8  | (x & 0xFF00FF00FF00FF00) >> 8;
return x;
}

 

 

Edited by KaFu
Posted

Nevertheless, I do not trust the proposed code ( that I gave you ) is a solution with AutoIt.  The ASM code simply calls (in a few nano secs) the SendMessageW API.  So why does it seem to work ? You ask me.  Well it is not obvious to answer since I do not own the core AutoIt code.  But my take on it, will go those few options :

1- the latency between AutoIt statement is larger than the time it takes to execute the ASM code

2- for "not that I know of" reason, the ASM routine is uninterruptible state

3- Windows execute ASM functions into a separate thread

Anyway, it was fun to look deeper into this challenging thread...Good luck !

Posted
Quote

 

DllCallbackRegister depends heavily on the stability of the AutoIt runtime environment.

The timer thread can run in parallel to the main AutoIt loop → threading conflicts and race conditions are possible.

AutoIt is not thread-safe, i.e. functions called in parallel threads (like by CreateTimerQueueTimer) can lead to crashes or undefined behavior. The _CallBackFunction function potentially runs outside the AutoIt context → e.g. problems with access to global variables or even crashes.

If you don't have an absolute need to use the Windows API timers with DllCallbackRegister, switch to AdlibRegister or TimerDiff-based loops. They are simpler, more stable and natively suitable for AutoIt.

 

 

 

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...