MattyD Posted 9 hours ago Posted 9 hours ago (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. expandcollapse popup;~ #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 9 hours ago by MattyD genius257, argumentum and KaFu 3
argumentum Posted 9 hours ago Posted 9 hours ago (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 Thanks for the code 👍 Edited 8 hours ago by argumentum nicer v4 🤯 MattyD 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
MattyD Posted 9 hours ago Author Posted 9 hours ago No worries! Thanks . I just stole the example from DllCallbackRegister() 😮 argumentum 1
MattyD Posted 6 hours ago Author Posted 6 hours ago 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. KaFu and argumentum 2
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now