Jump to content

Recommended Posts

Posted (edited)

 This is a follow-on from this thread

Hopefully it'll help with callbacks that ordinarily would crash AutoIt.  There is a bit of overhead here, so if a normal callback works - obviously use that!

I've reworked the function a bit from the other post..

  • So this now works for both x86 & x64.
  • I'm using the same syntax as DllStructCreate() - using tags etc.
  • Datatypes are better respected (based on the tag) - we were assuming int earlier .
  • Supports up to 10 parameters.
  • return values are preserved and are passed back to the caller.

Todo:

  • There's no cleanup funcs yet (deregister callback etc.). 
  • Need to check robustness across datatypes,  (doubles in x86 could potentially be a problem, need to check!)
  • Need error checking!
  • Need to manage the code buffer better - there's technically an overflow risk there. 
;~ #AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=N

#include <winapi.au3>
#include <Memory.au3>

;~ ;----------------------    Test Area    -------------------------

#include <MsgBoxConstants.au3>

; Create callback function.
Local $pCallback = _RegCallback("_EnumWindowsProc", "int", "hwnd;lparam")

; Call EnumWindows.
DllCall("user32.dll", "int", "EnumWindows", "ptr", $pCallback, "lparam", 10)

; Callback Procedure
Func _EnumWindowsProc($hWnd, $lParam)
    ; If the Title is empty or if the window is not visible then continue enumeration.
    If WinGetTitle($hWnd) = "" Or BitAND(WinGetState($hWnd), 2) = 0 Then Return 1

    Local $iRes = MsgBox(($MB_OKCANCEL + $MB_SYSTEMMODAL), _
            WinGetTitle($hWnd), "$hWnd=" & $hWnd & @CRLF & _
            "$lParam=" & $lParam & @CRLF & _
            "$hWnd(type)=" & VarGetType($hWnd))

    If $iRes <> $IDOK Then Return 0 ; Cancel/Close button clicked, return 0 to stop enumeration.

    Return 1 ; Return 1 to continue enumeration.
EndFunc   ;==>_EnumWindowsProc

;~ ;-----------------------------------------------------------------


Func _RegCallback($sFunction, $sReturn, $tagArgs)
    Local $iBuffSize = 512
    Local Static $aX64Lookup[][] = [ _
        ["48894C2408", "4889542410", "4C89442418", "4C894C2420"], _         ; 64 bit int
        ["894C2408", "89542410", "4489442418", "44894C2420"], _             ; 32 bit int
        ["66894C2408", "6689542410", "664489442418", "6644894C2420"], _     ; 16 bit int
        ["884C2408", "88542410", "4488442418", "44884C2420"], _             ;  8 bit int
        ["F20F11442408", "F20F114C2410", "F20F11542418", "F20F115C2420"], _ ; 64 bit float
        ["F30F11442408", "F30F114C2410", "F30F11542418", "F30F115C2420"]]   ; 32 bit float

    Local Static $hGUI
    Local Static $iMsg = $WM_APP
    Local Static $pSendMessage = _WinAPI_GetProcAddress(_WinAPI_GetModuleHandle("user32.dll"), "SendMessageW")

    If Not $hGUI Then
        $hGUI = GUICreate("RegisterSyncCallback_Msg", 10, 10)
        GUIRegisterMsg($iMsg, "__CallbackRelay")
    EndIf

    Local $pCodePoint = _MemVirtualAlloc(0, $iBuffSize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    Local $tCodeBuffer = DllStructCreate(StringFormat("byte Data[%d]", $iBuffSize), $pCodePoint)

    Local $hUserCallback = DllCallbackRegister($sFunction, $sReturn, $tagArgs)
    Local $pUserCallback = DllCallbackGetPtr($hUserCallback)

    Local $aArgsSplit = StringSplit($tagArgs, ";", $STR_NOCOUNT)
    Local $iNumParams = UBound($aArgsSplit)

    Local $tBin, $dBin, $sBin = "0x"

    If @AutoItX64 Then

        For $i = 0 To 3
            If $i = $iNumParams Then ExitLoop

            Switch StringLower(StringStripWS($aArgsSplit[$i], 8)) 
                Case "int64", "uint64", "string", "ptr", "handle", "hwnd", "str", "wstr"
                    $sBin &= $aX64Lookup[0][$i]
                Case "int_ptr", "long_ptr", "lresult", "lparam", "uint_ptr", "ulong_ptr", "dword_ptr", "wparam"
                    $sBin &= $aX64Lookup[0][$i]
                Case "int", "uint", "dword", "long", "ulong", "bool"
                    $sBin &= $aX64Lookup[1][$i]
                Case "word", "short", "ushort"
                    $sBin &= $aX64Lookup[2][$i]
                Case "byte", "boolean"
                    $sBin &= $aX64Lookup[3][$i]
                Case "double"
                    $sBin &= $aX64Lookup[4][$i]
                Case "float", "single"
                    $sBin &= $aX64Lookup[5][$i]
            EndSwitch
        Next
        Local $iParamDumpLen = (StringLen($sBin) - 2)/2

        $sBin &= "4883EC2848B90000000000000000BA000000004C8D44243049B9000000000000000048B80000000000000000FFD04883C428C3"
        $dBin = Binary($sBin)
        DllStructSetData($tCodeBuffer, 1, $dBin)
        $tBin = DllStructCreate(StringFormat("align 1;byte[%d];hwnd hGUI;byte;uint iMsg;byte[7];ptr pCallbackInfo;byte[2]" & _
                ";ptr pSendMessage;byte[7];struct;ptr pCallback;ptr pRetType;ptr ptagArgs;wchar sRetType[%d];wchar tagArgs[%d];endstruct", _
                $iParamDumpLen + 6, StringLen($sReturn) + 1, StringLen($tagArgs) + 1), $pCodePoint)

        $tBin.hGUI = $hGUI
        $tBin.iMsg = $iMsg
        $tBin.pCallbackInfo = Ptr($pCodePoint + $iParamDumpLen + 51)
        $tBin.pSendMessage = $pSendMessage
        $tBin.pCallback = $pUserCallback
        $tBin.pRetType = DllStructGetPtr($tBin, "sRetType")
        $tBin.ptagArgs = DllStructGetPtr($tBin, "tagArgs")
        $tBin.sRetType = $sReturn
        $tBin.tagArgs = $tagArgs
    Else
        $sBin = "0x8D44240468000000005068000000006800000000B800000000FFD0C20000"
        $dBin = Binary($sBin)
        DllStructSetData($tCodeBuffer, 1, $dBin)
        $tBin = DllStructCreate(StringFormat("align 1;byte[5];ptr pCallbackInfo;byte[2];uint iMsg;byte;hwnd hGUI;byte;ptr pSendMessage;byte[3];ushort iPopBytes;" & _
                "struct;ptr pCallback;ptr pRetType;ptr ptagArgs;wchar sRetType[%d];wchar tagArgs[%d];endstruct", StringLen($sReturn) + 1, StringLen($tagArgs) + 1), $pCodePoint)

        $tBin.hGUI = $hGUI
        $tBin.iMsg = $iMsg
        $tBin.pCallbackInfo = Ptr($pCodePoint + 30)
        $tBin.pSendMessage = $pSendMessage
        $tBin.iPopBytes = 4 * $iNumParams
        $tBin.pCallback = $pUserCallback
        $tBin.pRetType = DllStructGetPtr($tBin, "sRetType")
        $tBin.ptagArgs = DllStructGetPtr($tBin, "tagArgs")
        $tBin.sRetType = $sReturn
        $tBin.tagArgs = $tagArgs

    EndIf

    Return $pCodePoint
EndFunc

Func __CallbackRelay($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Const $PTR_LEN = (@AutoItX64 ? 8 : 4)
    Local $tCallbackInfo = DllStructCreate("struct;ptr pCallback;ptr pRetType;ptr ptagArgs;endstruct", $lParam)

    Local $pArgs = $wParam
    Local $pUserFunc = $tCallbackInfo.pCallback
    Local $pRetType = $tCallbackInfo.pRetType
    Local $ptagArgs = $tCallbackInfo.ptagArgs

    Local $tRetType = DllStructCreate(StringFormat("wchar tagRetType[%d]", _WinAPI_StringLenW($pRetType)), $pRetType)
    Local $sRetType = $tRetType.tagRetType

    Local $ttagArgs = DllStructCreate(StringFormat("wchar tagArgs[%d]", _WinAPI_StringLenW($ptagArgs)), $ptagArgs)
    Local $tagArgs = $ttagArgs.tagArgs

    Local $aRet, $aArgTypes = StringSplit($tagArgs, ";", $STR_NOCOUNT)
    Switch UBound($aArgTypes)
        Case 0
            $aRet = DllCallAddress($sRetType, $pUserFunc)
        Case 1
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]))
        Case 2
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]))

        Case 3
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[1]))
        Case 4
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]))
        Case 5
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]), _
                    $aArgTypes[4], __GetParam($pArgs + 4*$PTR_LEN, $aArgTypes[4]))
        Case 6
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]), _
                    $aArgTypes[4], __GetParam($pArgs + 4*$PTR_LEN, $aArgTypes[4]), _
                    $aArgTypes[5], __GetParam($pArgs + 5*$PTR_LEN, $aArgTypes[5]))
        Case 7
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]), _
                    $aArgTypes[4], __GetParam($pArgs + 4*$PTR_LEN, $aArgTypes[4]), _
                    $aArgTypes[5], __GetParam($pArgs + 5*$PTR_LEN, $aArgTypes[5]), _
                    $aArgTypes[6], __GetParam($pArgs + 6*$PTR_LEN, $aArgTypes[6]))
        Case 8
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]), _
                    $aArgTypes[4], __GetParam($pArgs + 4*$PTR_LEN, $aArgTypes[4]), _
                    $aArgTypes[5], __GetParam($pArgs + 5*$PTR_LEN, $aArgTypes[5]), _
                    $aArgTypes[6], __GetParam($pArgs + 6*$PTR_LEN, $aArgTypes[6]), _
                    $aArgTypes[7], __GetParam($pArgs + 7*$PTR_LEN, $aArgTypes[7]))
        Case 9
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]), _
                    $aArgTypes[4], __GetParam($pArgs + 4*$PTR_LEN, $aArgTypes[4]), _
                    $aArgTypes[5], __GetParam($pArgs + 5*$PTR_LEN, $aArgTypes[5]), _
                    $aArgTypes[6], __GetParam($pArgs + 6*$PTR_LEN, $aArgTypes[6]), _
                    $aArgTypes[7], __GetParam($pArgs + 7*$PTR_LEN, $aArgTypes[7]), _
                    $aArgTypes[8], __GetParam($pArgs + 8*$PTR_LEN, $aArgTypes[8]))
        Case 10
            $aRet = DllCallAddress($sRetType, $pUserFunc, _
                    $aArgTypes[0], __GetParam($pArgs, $aArgTypes[0]), _
                    $aArgTypes[1], __GetParam($pArgs + $PTR_LEN, $aArgTypes[1]), _
                    $aArgTypes[2], __GetParam($pArgs + 2*$PTR_LEN, $aArgTypes[2]), _
                    $aArgTypes[3], __GetParam($pArgs + 3*$PTR_LEN, $aArgTypes[3]), _
                    $aArgTypes[4], __GetParam($pArgs + 4*$PTR_LEN, $aArgTypes[4]), _
                    $aArgTypes[5], __GetParam($pArgs + 5*$PTR_LEN, $aArgTypes[5]), _
                    $aArgTypes[6], __GetParam($pArgs + 6*$PTR_LEN, $aArgTypes[6]), _
                    $aArgTypes[7], __GetParam($pArgs + 7*$PTR_LEN, $aArgTypes[7]), _
                    $aArgTypes[8], __GetParam($pArgs + 8*$PTR_LEN, $aArgTypes[8]), _
                    $aArgTypes[9], __GetParam($pArgs + 9*$PTR_LEN, $aArgTypes[9]))
    EndSwitch

    Return $aRet[0]
EndFunc

Func __GetParam($pParam, $sType)
    Return DllStructGetData(DllStructCreate($sType, $pParam), 1)
EndFunc
Edited by MattyD
Posted (edited)
...
Func _EnumWindowsProc($hWnd, $lParam)
    Local Static $WS_VISIBLE = 0x10000000, $WS_CHILD = 0x40000000, $iCount = 0, _
            $GWL_EXSTYLE = -20, $GWL_STYLE = -16, $WS_EX_TOOLWINDOW = 0x00000080, _
            $WS_EX_APPWINDOW = 0x00040000, $iFound = 0, $hTimer = TimerInit()

    $iCount += 1

    Local $aRet = DllCall("user32.dll", "long", "GetWindowLongW", "hwnd", $hWnd, "int", $GWL_STYLE)
    If Not @error And Not BitAND($aRet[0], $WS_VISIBLE) Or BitAND($aRet[0], $WS_CHILD) Then Return 1

    $aRet = DllCall("user32.dll", "long", "GetWindowLongW", "hwnd", $hWnd, "int", $GWL_EXSTYLE)
    If Not @error And BitAND($aRet[0], $WS_EX_TOOLWINDOW) And Not BitAND($aRet[0], $WS_EX_APPWINDOW) Then Return 1

    Local $sTitle = _GetWindowTextW($hWnd)
    If $sTitle = "" Then Return 1

    $iFound += 1

    ConsoleWrite("-  " & $iFound & " / " & $iCount & @TAB & TimerDiff($hTimer) & @TAB & " [Handle: " & $hWnd & "] [Title: " & $sTitle & "]" & @CRLF)
    $hTimer = TimerInit()

    Return 1 ; Return 1 to continue enumeration.
EndFunc   ;==>_EnumWindowsProc

Func _GetWindowTextW($hWnd)
    Local Static $iStrLen = 512, $tBuffer = DllStructCreate("wchar[" & $iStrLen & "]")
    Local $aRet = DllCall("user32.dll", "int", "GetWindowTextW", _
            "hwnd", $hWnd, "struct*", $tBuffer, "int", $iStrLen)
    If Not @error And $aRet[0] > 0 Then Return DllStructGetData($tBuffer, 1)
    Return ""
EndFunc   ;==>_GetWindowTextW
...
-  31 / 416 0.2691   [Handle: 0x000205D8] [Title: *@@ (4186)    $e_GT_Connect - Notepad]
>
+>04:10:56 AutoIt3 ended. rc:0
+>04:10:57 AutoIt3Wrapper Finished.
>Exit code: 0    Time: 0.6310

...this is faster :D 
Thanks for the code 👍

Edited by argumentum
nicer v4 🤯

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting  image.gif.922e3a93535f431de08b31ee669cc446.gif
autoit_scripter_blue_userbar.png

Posted

Ok, I tried this with a known crashy callback due to multithreading, and there is some improvement - but its still not anywhere near stable.

But there's more things to try..  We can use PostMessage instead of sendmessage.  In that case the proxy just dumps the WM_ message and runs, and the msg pump eventually picks it for processing. So this puts some distance between means the main thread and whatever workers there are.

It's not as easy as swapping out the sendmessage function ptr though. At the moment the callback args (wparam) are located in the proxy's stack...  This is no good for postmessage because the stack's already unwound by the time the msgloop get to it. So we need to stash that data into RAM somewhere, and the WM_ handler would have to be responsible for releasing that memory.

Also, as soon as we make the mechanism asynchronous, the caller can't get a return code from the callback.  So there may be issues that arise from that too.

But anyway - It'll keep me occupied for a little bit.

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...