Sign in to follow this  
Followers 0

CallBack - RegisterCallback function

33 posts in this topic




Posted

I noticed AutoHotkey added RegisterCallback(), which creates a machine-code address that when called, redirects the call to a function in the script.

This could be usefull adition to Autoit.

Look at

http://www.autohotkey.com/changelog/

http://www.autohotkey.com/docs/commands/RegisterCallback.htm

Second here.

On their forum I found tiny dll easily adaptable to use with GUIRegisterMsg (maybe something like was posted here, not found), but native dll-less feature is definitely preferable.

Share this post


Link to post
Share on other sites

Posted

Second here.

On their forum I found tiny dll easily adaptable to use with GUIRegisterMsg (maybe something like was posted here, not found), but native dll-less feature is definitely preferable.

Awesome!!

Here is the link to that post.

Can somebody experienced translate this EnumWindows.ahk example using callback.dll to EnumWindows.au3 please?

pid:=DllCall("GetCurrentProcessId","Uint")
DetectHiddenWindows, On
hwnd:=WinExist("ahk_pid " . pid)

hHookModule := DllCall("LoadLibrary", "str", "callback\callback.dll")
EnumWindowsProcStub:=DllCall("callback\callback.dll\callbackit", "UInt", 2,"UInt", hwnd, "UInt", 0x542, "UInt", 0)
OnMessage(0x542, "EnumWindowsProc")
GoSub doit
OnExit Tidyup

AppsKey & F11::
doit:
	out:=
	DllCall("EnumWindows","UInt",EnumWindowsProcStub,"UInt",0)
	
	EnumWindowsProc(wParam, lParam, msg, hwnd) {
		global out
		winid:=GetDeRefInteger(lParam)
		otherparam:=GetDeRefInteger(lParam+4)
		DetectHiddenWindows, On
		WinGetTitle, text, ahk_id %winid%
		WinGetClass, class, ahk_id %winid%
		out.="HWND: ". winid . "`tTitle: " . text . "`tClass: " . class . "`n"
		return 1
	}
;Sleep 5000
	MsgBox %out%
return

;Tidyness
Tidyup:
DllCall("GlobalFree", "UInt", EnumWindowsProcStub)
DllCall("FreeLibrary", "Uint", hHookModule)
ExitApp

GetDeRefInteger(pSource, pIsSigned = false, pSize = 4)
; pSource is an integer pointer to a raw/binary integer
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
{
	Loop %pSize% ; Build the integer by adding up its bytes.
		result += *(pSource + A_Index-1) << 8*(A_Index-1)
	if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
		return result ; Signed vs. unsigned doesn't matter in these cases.
; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
	return -(0xFFFFFFFF - result + 1)
}

Share this post


Link to post
Share on other sites

Posted

Awesome!!

Here is the link to that post.

Can somebody experienced translate this EnumWindows.ahk example using callback.dll to EnumWindows.au3 please?

Here it is (a bit simplified).

Global $out

$hwnd = GUICreate("")

$WM_AUTOIT_CALLBACK = DllCall("user32", "int", "RegisterWindowMessage")
$WM_AUTOIT_CALLBACK = $WM_AUTOIT_CALLBACK[0]

$hHookModule = DllCall("kernel32", "int", "LoadLibrary", "str", "callback.dll")
$hHookModule = $hHookModule[0]

$EnumWindowsProcStub = DllCall("callback.dll", "int", "callbackit", "uint", 2,"uint", $hwnd, "uint", $WM_AUTOIT_CALLBACK, "uint", 0)
$EnumWindowsProcStub = $EnumWindowsProcStub[0]

GUIRegisterMsg($WM_AUTOIT_CALLBACK, "EnumWindowsProc")

DllCall("user32", "int", "EnumWindows","uint", $EnumWindowsProcStub, "uint", 0)
	
Func EnumWindowsProc($hWnd, $msg, $wParam, $lParam) 
	$struct = DllStructCreate("int;int", $lParam)
	$out &= "Title: " & WinGetTitle(HWND(DllStructGetData($struct, 1))) & @CRLF
	Return 1
EndFunc

MsgBox (0, "Windows", $out) 

Func OnAutoitExit()
	DllCall("kernel32", "int", "GlobalFree", "uint", $EnumWindowsProcStub)
	DllCall("kernel32", "int", "FreeLibrary", "Uint", $hHookModule)
EndFunc

Share this post


Link to post
Share on other sites

Posted

I had some problem getting the code to run. I swapped out a few of the DllCall functions to use the Auto3Lib versions and that seemed to fix the problem:

#include <A3LMemory.au3>

Global $hGUI, $hHook, $iMessage, $pStub

$hGUI = GUICreate("")

$iMessage = _API_RegisterWindowMessage("AutoItCallback")
$hHook    = _API_LoadLibrary("CallBack.dll")
$pStub    = DllCall("CallBack.dll", "int", "callbackit", "uint", 2, "uint", $hGUI, "uint", $iMessage, "uint", 0)
$pStub    = $pStub[0]

GUIRegisterMsg($iMessage, "EnumWindowsProc")
DllCall("User32.dll", "int", "EnumWindows", "uint", $pStub, "uint", 0)

Func EnumWindowsProc($hWnd, $msg, $wParam, $lParam)
  Local $tStruct, $sText

  $tStruct = DllStructCreate("int;int", $lParam)
  $sText  = WinGetTitle(HWND(DllStructGetData($tStruct, 1)))
  if $sText <> "" then _Lib_ConsoleWrite($sText)
  Return 1
EndFunc

Func OnAutoitExit()
  _Mem_GlobalFree ($pStub)
  _API_FreeLibrary($hHook)
EndFunc

The Callback.dll needs to be in the script directory.

Share this post


Link to post
Share on other sites

Posted

Great! It looks very promising.

Thanks Lazycat and PaulIA. :rolleyes:

Share this post


Link to post
Share on other sites

Posted

mmm it could be the solution for the listview sort :rambo:

if anyone can do it :rolleyes:

Share this post


Link to post
Share on other sites

Posted

mmm it could be the solution for the listview sort :rambo:

if anyone can do it :rolleyes:

For ListView sort is GUICtrlRegisterListViewSort() already in Autoit.

Share this post


Link to post
Share on other sites

Posted

!! never noticed this function !!

thx

Share this post


Link to post
Share on other sites

Posted

What about some improvements:

1) use @AutoItPID to get Hwnd --> for scripts without GUI

2) please make your suggestions here for API calls using CallVack

to make try if it works also on other functions then only EnumWindows,

for example: ReadDirectoryChangesW (as somebody said in AHK forum),...

3) make some include or UDF version --> something like :

CallBack_UDF.au3:

#include <A3LMemory.au3>

Global $hHook, $iMessage, $pStub

Func _RegisterCallBack($hWnd, $FunctioName
    $iMessage = _API_RegisterWindowMessage("AutoItCallback")
    $hHook    = _API_LoadLibrary("CallBack.dll")
    $pStub    = DllCall("CallBack.dll", "int", "callbackit", "uint", 2, "uint", $hWnd, "uint", $iMessage, "uint", 0)
    $pStub    = $pStub[0]

    GUIRegisterMsg($iMessage, $FunctioName)
    
    Return $pStub
EndFunc

Func OnAutoitExit()
  _Mem_GlobalFree ($pStub)
  _API_FreeLibrary($hHook)
EndFunc

Share this post


Link to post
Share on other sites

Posted

thats nice.

i tried to do such a thing but i failed on adjusting the stack pointer right :rolleyes:

Can anyone enlighten me on why the stub is allocated in global memory?

Im not sure but i think its a bad thing to do since global memory is limited...

Anyway i played with it too and wrote some wrappers that do not rely on Auto3Lib.

No offence paul, i really appreciate your work but since there is only one function needed...

Global $g_hDll_CallBack = DllOpen("Callback.dll")

Func _DllCallBackAlloc($nParameters, $hWnd, $msg, $wParam = 0)
    Local $hStub
    If $g_hDll_CallBack < 0 Then Return SetError(1, 0, 0)
    $hStub = DllCall($g_hDll_CallBack, "ptr", "callbackit", "uint", $nParameters, "hwnd", $hWnd, "uint", $msg, "ptr", $wParam)
    If @error Then Return SetError(2, 0, 0)
    If $hStub[0] = 0 Then Return SetError(2, 0, 0)
    Return $hStub[0]
EndFunc   ;==>_DllCallBackAlloc

Func _DllCallBackFree(ByRef $hStub)
    Local $aTmp
    If $hStub > 0 Then
        $aTmp = DllCall("kernel32.dll", "ptr", "GlobalFree", "ptr", $hStub)
        If @error Then Return SetError(1, 0, False)
        If $aTmp[0] = $hStub Then Return SetError(2, 0, False)
        If $aTmp[0] = 0 Then
            $hStub = 0
            Return True
        EndIf
        Return False
    EndIf
    Return True
EndFunc   ;==>_DllCallBackFree

;---------------------------

Global Const $WM_USER = 0x400
Global $hWnd_dummy = GUICreate(""), $sOut

GUIRegisterMsg($WM_USER + 1, "_EnumWindowStationProc")
Func _EnumWindowStationProc($hWnd, $msg, $wParam, $lParam)
    $Param = DllStructCreate("ptr;ptr", $lParam)
    $station = DllStructCreate("char[256]", DllStructGetData($Param, 1))
    $sOut &= DllStructGetData($station, 1) & @CRLF
    Return 1
EndFunc   ;==>_EnumWindowStationProc

$hStub_EnumWindowStationProc = _DllCallBackAlloc(2, $hWnd_dummy, $WM_USER + 1)
DllCall("user32.dll", "int", "EnumWindowStations", "ptr", $hStub_EnumWindowStationProc, "long", 0)
_DllCallBackFree($hStub_EnumWindowStationProc)

ConsoleWrite($sOut & @CRLF)

I was thinking about adding cdecl support, but i'm not really sure what i'm doing here...

so if i patch this (the stub created by the dll)

0015B4B8   E8 00000000	  CALL 0015B4BD
0015B4BD   58			   POP EAX
0015B4BE   8D90 2B000000	LEA EDX,DWORD PTR DS:[EAX+2B]
0015B4C4   8D4424 04		LEA EAX,DWORD PTR SS:[ESP+4]
0015B4C8   FF32			 PUSH DWORD PTR DS:[EDX]
0015B4CA   50			   PUSH EAX
0015B4CB   FF72 0C		  PUSH DWORD PTR DS:[EDX+C]
0015B4CE   FF72 08		  PUSH DWORD PTR DS:[EDX+8]
0015B4D1   FF72 04		  PUSH DWORD PTR DS:[EDX+4]
0015B4D4   FF15 85103001	CALL DWORD PTR DS:[1301085]			 ; SendMessageA
0015B4DA   5A			   POP EDX
0015B4DB   59			   POP ECX
0015B4DC   8D2494		   LEA ESP,DWORD PTR SS:[ESP+EDX*4]
0015B4DF   FFE1			 JMP ECX
0015B4E1   90			   NOP
into this
0015B4B8   E8 00000000	  CALL 0015B4BD
0015B4BD   58			   POP EAX
0015B4BE   8D90 2B000000	LEA EDX,DWORD PTR DS:[EAX+2B]
0015B4C4   8D4424 04		LEA EAX,DWORD PTR SS:[ESP+4]
0015B4C8   FF32			 PUSH DWORD PTR DS:[EDX]
0015B4CA   50			   PUSH EAX
0015B4CB   FF72 0C		  PUSH DWORD PTR DS:[EDX+C]
0015B4CE   FF72 08		  PUSH DWORD PTR DS:[EDX+8]
0015B4D1   FF72 04		  PUSH DWORD PTR DS:[EDX+4]
0015B4D4   FF15 85103001	CALL DWORD PTR DS:[1301085]			 ; SendMessageA
0015B4DA   5A			   POP EDX
0015B4DB   59			   POP ECX
0015B4DC   FFE1			 JMP ECX
stack looks good in the debugger but im still not sure :rambo:

any thoughts on that?

Share this post


Link to post
Share on other sites

Posted

Finally i see a light at the end of the (callback) tunnel...

This can be done without a dll too :rolleyes:

here is a proof of concept example:

#cs
   $sStub = "" _
   & "E800000000"   _     ;~    $ ==>    > E8 00000000      CALL 0015B4BD                            
   & "58"           _     ;~    $+5      > 58               POP EAX
   & "8D902B000000" _     ;~    $+6      > 8D90 2B000000    LEA EDX,DWORD PTR DS:[EAX+2B]
   & "8D442404"     _     ;~    $+C      > 8D4424 04        LEA EAX,DWORD PTR SS:[ESP+4]
   & "FF32"         _     ;~    $+10     > FF32             PUSH DWORD PTR DS:[EDX]
   & "50"           _     ;~    $+12     > 50               PUSH EAX                                 ; lparam
   & "FF720C"       _     ;~    $+13     > FF72 0C          PUSH DWORD PTR DS:[EDX+C]                ; wparam $+3c
   & "FF7208"       _     ;~    $+16     > FF72 08          PUSH DWORD PTR DS:[EDX+8]                ; msg $+38
   & "FF7204"       _     ;~    $+19     > FF72 04          PUSH DWORD PTR DS:[EDX+4]                ; hwnd $+34
   & "E8!!!!!!!!90" _     ;~    $+1C     > FF15 85103001    CALL DWORD PTR DS:[1301085]              ; Call Sendmessage
   & "5A"           _     ;~    $+22     > 5A               POP EDX
   & "59"           _     ;~    $+23     > 59               POP ECX
   & "8D2494"       _     ;~    $+24     > 8D2494           LEA ESP,DWORD PTR SS:[ESP+EDX*4]         ; Adjust stack by $+30 (stdcall only)
   & "FFE1"         _     ;~    $+27     > FFE1             JMP ECX
   & "90"                 ;~    $+29     > 90               NOP
                          ;~    $+2A     > 000000000000                                             ; 6 bytes nothing
                          ;~    $+30     > 02000000                                                 ; nParameters
                          ;~    $+34     > D6080100                                                 ; hwnd
                          ;~    $+38     > 01040000                                                 ; msg
                          ;~    $+3C     > 00000000                                                 ; wparam
   
   ; stdcall : E800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8!!!!!!!!905A598D2494FFE190
   ; cdecl   : E800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8!!!!!!!!905A59909090FFE190
   ;           0x1D (29.)                                                0x04    0x09
   ;           0x56 (86.)
#ce

$WM_USER = 0x400
$hWnd_dummy = GUICreate("")
$sOut = ""

$hGlobal = DllCall("kernel32.dll","ptr","GlobalAlloc","uint",0,"dword",96)
$hUser32 = DllCall("kernel32.dll","ptr","LoadLibrary","str","user32.dll")
$pSendMessage = DllCall("kernel32.dll","ptr","GetProcAddress","ptr",$hUser32[0],"str","SendMessageA")
$pRelSendMessage = $pSendMessage[0] - ($hGlobal[0] + 0x21)

$vStub = DllStructCreate("ubyte[29];"       _ ; 1 Stub Part 1
                       & "ptr;"             _ ; 2 Address of SendMessage relative to $+21 (pTo - $+21)
                       & "ubyte[9];"        _ ; 3 Stub Part 2
                       & "byte[6];"         _ ; 4 6 bytes nothing
                       & "long;"            _ ; 5 nParams
                       & "hwnd;"            _ ; 6 hwnd
                       & "uint;"            _ ; 7 msg
                       & "uint;",$hGlobal[0]) ; 8 wparam

DllStructSetData($vStub,1,Binary("0xE800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8"))
DllStructSetData($vStub,2,$pRelSendMessage)
DllStructSetData($vStub,3,Binary("0x905A598D2494FFE190"))
DllStructSetData($vStub,4,Binary("0x000000000000"))
DllStructSetData($vStub,5,2)
DllStructSetData($vStub,6,$hWnd_dummy)
DllStructSetData($vStub,7,$WM_USER+1)
DllStructSetData($vStub,8,0)

GUIRegisterMsg($WM_USER + 1, "_EnumWindowStationProc")
Func _EnumWindowStationProc($hWnd, $msg, $wParam, $lParam)
    $Param = DllStructCreate("ptr;ptr", $lParam)
    $station = DllStructCreate("char[256]", DllStructGetData($Param, 1))
    $sOut &= DllStructGetData($station, 1) & @CRLF
    Return 1
EndFunc   ;==>_EnumWindowStationProc

DllCall("user32.dll", "int", "EnumWindowStations", "ptr", $hGlobal[0], "long", 0)

ConsoleWrite($sOut & @CRLF)

DllCall("kernel32.dll", "ptr", "GlobalFree", "ptr", $hGlobal[0])
at least it works for me :rambo:

but i have no practice on a level that low and i'm only 90% sure about what i'm doing here so

if someone with asm experience could take a look at it...

I would really appreciate that :x

Share this post


Link to post
Share on other sites

Posted

You guys ROCK! :rolleyes:

Please make it an official UDF.

To make it a safer UDF I think a discussion should be started on how multiple OnAutoItExit functions could be registered with the script engine. It would be so much simpler if a UDF's file could automatically clean up after it self. The discussion probably deserves it's own topic, but maybe you will keep it in mind and consider possible solutions?

I have wrapped it up a bit for my own understanding and added a simple EnumChildWindows sample.

#region - Win32EnumsStub
; ============================================================================
;
; ============================================================================

Global $_CreateCallBackStubCounter = 0
Func _CreateCallBackStub()
#cs
   $sStub = "" _
   & "E800000000"   _     ;~    $ ==>    > E8 00000000      CALL 0015B4BD                            
   & "58"           _     ;~    $+5      > 58               POP EAX
   & "8D902B000000" _     ;~    $+6      > 8D90 2B000000    LEA EDX,DWORD PTR DS:[EAX+2B]
   & "8D442404"     _     ;~    $+C      > 8D4424 04        LEA EAX,DWORD PTR SS:[ESP+4]
   & "FF32"         _     ;~    $+10     > FF32             PUSH DWORD PTR DS:[EDX]
   & "50"           _     ;~    $+12     > 50               PUSH EAX                                 ; lparam
   & "FF720C"       _     ;~    $+13     > FF72 0C          PUSH DWORD PTR DS:[EDX+C]                ; wparam $+3c
   & "FF7208"       _     ;~    $+16     > FF72 08          PUSH DWORD PTR DS:[EDX+8]                ; msg $+38
   & "FF7204"       _     ;~    $+19     > FF72 04          PUSH DWORD PTR DS:[EDX+4]                ; hwnd $+34
   & "E8!!!!!!!!90" _     ;~    $+1C     > FF15 85103001    CALL DWORD PTR DS:[1301085]              ; Call Sendmessage
   & "5A"           _     ;~    $+22     > 5A               POP EDX
   & "59"           _     ;~    $+23     > 59               POP ECX
   & "8D2494"       _     ;~    $+24     > 8D2494           LEA ESP,DWORD PTR SS:[ESP+EDX*4]         ; Adjust stack by $+30 (stdcall only)
   & "FFE1"         _     ;~    $+27     > FFE1             JMP ECX
   & "90"                 ;~    $+29     > 90               NOP
                          ;~    $+2A     > 000000000000            ; 6 bytes nothing
                          ;~    $+30     > 02000000             ; nParameters
                          ;~    $+34     > D6080100             ; hwnd
                          ;~    $+38     > 01040000             ; msg
                          ;~    $+3C     > 00000000             ; wparam
   
   ; stdcall : E800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8!!!!!!!!905A598D2494FFE190
   ; cdecl   : E800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8!!!!!!!!905A59909090FFE190
   ;           0x1D (29.)                                                0x04    0x09
   ;           0x56 (86.)
#ce
    $_CreateCallBackStubCounter += 1
    $WM_USER = 0x400 ; TODO: Can we check that the message $WM_USER + $ID is not used allready?
    $hWnd_dummy = GUICreate("") ;TODO: Check if user has a window allready and use that or make sure we switch back to current window when done here?
    $hGlobal = DllCall("kernel32.dll","ptr","GlobalAlloc","uint",0,"dword",96)
    $hUser32 = DllCall("kernel32.dll","ptr","LoadLibrary","str","user32.dll")
    $pSendMessage = DllCall("kernel32.dll","ptr","GetProcAddress","ptr",$hUser32[0],"str","SendMessageA")
    $pRelSendMessage = $pSendMessage[0] - ($hGlobal[0] + 0x21)

    $vStub = DllStructCreate("ubyte[29];"       _ ; 1 Stub Part 1
                           & "ptr;"             _ ; 2 Address of SendMessage relative to $+21 (pTo - $+21)
                           & "ubyte[9];"        _ ; 3 Stub Part 2
                           & "byte[6];"         _ ; 4 6 bytes nothing
                           & "long;"            _ ; 5 nParams
                           & "hwnd;"            _ ; 6 hwnd
                           & "uint;"            _ ; 7 msg
                           & "uint;",$hGlobal[0]) ; 8 wparam

    DllStructSetData($vStub,1,Binary("0xE800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8"))
    DllStructSetData($vStub,2,$pRelSendMessage)
    DllStructSetData($vStub,3,Binary("0x905A598D2494FFE190"))
    DllStructSetData($vStub,4,Binary("0x000000000000"))
    DllStructSetData($vStub,5,2)
    DllStructSetData($vStub,6,$hWnd_dummy)
    DllStructSetData($vStub,7,$WM_USER + $_CreateCallBackStubCounter) ;$WM_USER+$ID)
    DllStructSetData($vStub,8,0)
    Local $r[2]
    $r[0] = $WM_USER + $_CreateCallBackStubCounter
    $r[1] = $hGlobal[0]
    return $r
EndFunc
#endregion
Global $cb1, $cb2, $cb3 ;Has to be global to be able to free data. 
;         Used by *proc samples so placed in the midle of the code
#region - Win32EnumProcs
Func _EnumWindowStationsProc($hWnd, $msg, $wParam, $lParam)
    Local $station, $sOut
    $Param = DllStructCreate("ptr;ptr", $lParam)
    $station = DllStructCreate("char[256]", DllStructGetData($Param, 1))
    $sOut = DllStructGetData($station, 1) & @CRLF
    ConsoleWrite("_EnumWindowStationsProc: $sOut:=" & $sOut & @CRLF)
    Return 1
EndFunc   ;==>_EnumWindowStationsProc

Func _EnumWindowsProc($hWnd, $msg, $wParam, $lParam)
    Local $tStruct, $sOut

    $tStruct = DllStructCreate("int;int", $lParam)
    $sOut  = WinGetTitle(HWND(DllStructGetData($tStruct, 1)))
    if $sOut <> "" then 
        ; NOTE: Calling external global variable
        DllCall("user32.dll", "int", "EnumChildWindows", "hwnd", HWND(DllStructGetData($tStruct, 1)), "ptr", $cb3[1], "long", 0 )
        ConsoleWrite("_EnumWindowsProc: $hWnd:=" & DllStructGetData($tStruct, 1) & ", $lParam:=" & DllStructGetData($tStruct, 2) & ", $sOut:=" & $sOut & @CRLF)
    EndIf
  Return 1
EndFunc
Func _EnumChildWindowsProc($hWnd, $msg, $wParam, $lParam)
    Local $tStruct, $sOut
    $tStruct = DllStructCreate("int;int", $lParam)
    ConsoleWrite("_EnumChildWindowsProc: $hWnd:=" & HWND(DllStructGetData($tStruct, 1)) & ", $lParm:=" & HWND(DllStructGetData($tStruct, 2)) & @CRLF )
    Return 1
EndFunc
#endregion - Win32EnumProcs

Func Main()
    HotKeySet("{ESC}", "Terminate")
    ; Create stub, Register message, call the api
    $cb1 = _CreateCallBackStub()
    GUIRegisterMsg($cb1[0], "_EnumWindowStationsProc")
    DllCall("user32.dll", "int", "EnumWindowStations", "ptr", $cb1[1], "long", 0)
    ;
    ; Create stub, Register message, call the api
    ; Used by $cb2 To enumerate childwindows    
    $cb3 = _CreateCallBackStub()
    GUIRegisterMsg($cb3[0], "_EnumChildWindowsProc")    
    ;   
    ; Create stub, Register message, call the api
    $cb2 = _CreateCallBackStub()
    GUIRegisterMsg($cb2[0], "_EnumWindowsProc")
    DllCall("user32.dll", "int", "EnumWindows", "ptr", $cb2[1], "long", 0)
    

    ; The normal application loop
    While 1
        Switch GuiGetMsg()
            Case -3
                ExitLoop
            Case 0
                Sleep(100)
        EndSwitch
    WEnd
    ;ConsoleWrite($sOut & @CRLF)
    Terminate()
EndFunc
Func Terminate()
    ; Make sure we clean up the callback stubs.
    ConsoleWrite("TERMINATE: Cleaning up" & @CRLF)
    If $cb1[1] <> 0 Then DllCall("kernel32.dll", "ptr", "GlobalFree", "ptr", $cb1[1])
    If $cb2[1] <> 0 Then DllCall("kernel32.dll", "ptr", "GlobalFree", "ptr", $cb2[1])
    If IsArray($cb3) Then 
        If ($cb3[1] <> 0) Then DllCall("kernel32.dll", "ptr", "GlobalFree", "ptr", $cb3[1])
    EndIf
    ConsoleWrite("TERMINATE: Cleaning up done" & @CRLF)
    Exit
EndFunc
; ========================================
Main()

Share this post


Link to post
Share on other sites

Posted

i dont know if it ever gets official but here is it in udf form...

Global $gp_DllCallBack_SendMessage = 0
Global $gh_DllCallBack_hUser32 = 0
Global $gi_DllCallBack_StubCount = 0

;===============================================================================
;
; Function Name:    _DllCallBackAlloc
; Description:      Allocates memory on global heap and creates a procedure
;                   which forwards a pointer to its arguments to a Window
; Parameter(s):     $nParameters - number of parameters
;                   $hWnd - handle of receiving window
;                   $msg - Window Message
;                   $wParam, Optional - wParam to pass to the handler procedure
;                   $fCedecl, Optional - use cdecl calling convention (default is stdcall)
; Requirement(s):
; Return Value(s):  Pointer to created stub or NULL on error
; @error Value(s):  1 = Error allocating memory from global heap
;                   2 = Error Loading User32.dll
;                   3 = Failed to get the address of SendMessage
;
; Author(s):
;
;===============================================================================
;
Func _DllCallBackAlloc($nParameters, $hWnd, $msg, $wParam = 0, $fcdecl = False)
    Local $hGlobal, $hUser32, $pSendMessage_RelAddr, $vStub
    $hGlobal = DllCall("kernel32.dll", "ptr", "GlobalAlloc", "uint", 0, "dword", 86) ; 86 = Size of stub
    If @error Then Return SetError(1, 0, 0)
    If $hGlobal[0] = 0 Then Return SetError(1, 0, 0)
    $hGlobal = $hGlobal[0]
    If $gp_DllCallBack_SendMessage = 0 Then
        $hUser32 = DllCall("kernel32.dll", "ptr", "LoadLibrary", "str", "user32.dll")
        If @error Then Return SetError(2, 0, 0)
        If $hUser32[0] = 0 Then SetError(2, 0, 0)
        $gh_DllCallBack_hUser32 = $hUser32[0]
        $pSendMessage = DllCall("kernel32.dll", "ptr", "GetProcAddress", "ptr", $gh_DllCallBack_hUser32, "str", "SendMessageA")
        If @error Then Return SetError(3, 0, 0)
        If $pSendMessage = 0 Then Return SetError(3, 0, 0)
        $gp_DllCallBack_SendMessage = $pSendMessage[0]
    EndIf
    $pSendMessage_RelAddr = $gp_DllCallBack_SendMessage - ($hGlobal + 0x21)
    $vStub = DllStructCreate("ubyte[29];" _ ; 1 Stub Part 1
             & "ptr;" _ ; 2 Address of SendMessage relative to $+21 (pTo - $+21)
             & "ubyte[9];" _ ; 3 Stub Part 2
             & "byte[6];" _ ; 4 6 bytes nothing
             & "long;" _ ; 5 nParams
             & "hwnd;" _ ; 6 hwnd
             & "uint;" _ ; 7 msg
             & "uint;", $hGlobal) ; 8 wparam
    #cs Stub Part 1
        $ ==>    > E8 00000000      CALL 0015B4BD
        $+5      > 58               POP EAX
        $+6      > 8D90 2B000000    LEA EDX,DWORD PTR DS:[EAX+2B]
        $+C      > 8D4424 04        LEA EAX,DWORD PTR SS:[ESP+4]
        $+10     > FF32             PUSH DWORD PTR DS:[EDX]
        $+12     > 50               PUSH EAX
        $+13     > FF72 0C          PUSH DWORD PTR DS:[EDX+C]
        $+16     > FF72 08          PUSH DWORD PTR DS:[EDX+8]
        $+19     > FF72 04          PUSH DWORD PTR DS:[EDX+4]
        $+1C     > E8 !!!!!!!!      CALL USER32.SendMessageA    ; $pSendMessage_RelAddr
    #ce
    DllStructSetData($vStub, 1, Binary("0xE800000000588D902B0000008D442404FF3250FF720CFF7208FF7204E8"))
    DllStructSetData($vStub, 2, $pSendMessage_RelAddr)
    If $fcdecl Then
        #cs Stub Part 2 - cdecl
            $+21     > 90               NOP
            $+22     > 5A               POP EDX
            $+23     > 59               POP ECX
            $+24     > 90               NOP
            $+25     > 90               NOP
            $+26     > 90               NOP
            $+27     > FFE1             JMP ECX
            $+29     > 90               NOP
        #ce
        DllStructSetData($vStub, 3, Binary("0x905A59909090FFE190"))
    Else
        #cs Stub Part 2 - stdcall
            $+21     > 90               NOP
            $+22     > 5A               POP EDX
            $+23     > 59               POP ECX
            $+24     > 8D2494           LEA ESP,DWORD PTR SS:[ESP+EDX*4]
            $+27     > FFE1             JMP ECX
            $+29     > 90               NOP
        #ce
        DllStructSetData($vStub, 3, Binary("0x905A598D2494FFE190"))
    EndIf
;~    $+2A     > 000000000000
    DllStructSetData($vStub, 4, Binary("0x000000000000"))
;~    $+30     > 00000000
    DllStructSetData($vStub, 5, $nParameters)
;~    $+34     > 00000000
    DllStructSetData($vStub, 6, $hWnd)
;~    $+38     > 00000000
    DllStructSetData($vStub, 7, $msg)
;~    $+3C     > 00000000
    DllStructSetData($vStub, 8, $wParam)
    $gi_DllCallBack_StubCount += 1
    Return $hGlobal
EndFunc   ;==>_DllCallBackAlloc


;===============================================================================
;
; Function Name:    _DllCallBackFree
; Description:      Frees memory from global heap allocated by _DllCallBackAlloc
; Parameter(s):     $hStub - Pointer to stub
; Requirement(s):
; Return Value(s):  On Success: True
;                   On Failure: False
; @error Value(s):  1 - Error freeing memory
;                   2 - Error freeing User32.dll
; Author(s):
;
;===============================================================================
;
Func _DllCallBackFree(ByRef $hStub)
    Local $aTmp
    If $hStub > 0 Then
        $aTmp = DllCall("kernel32.dll", "ptr", "GlobalFree", "ptr", $hStub)
        If @error Then Return SetError(1, 0, False)
        If $aTmp[0] = $hStub Then Return SetError(1, 0, False)
        If $aTmp[0] = 0 Then
            $hStub = 0
            $gi_DllCallBack_StubCount -= 1
            If $gi_DllCallBack_StubCount < 1 Then
                $gi_DllCallBack_StubCount = 0
                $aTmp = DllCall("kernel32.dll", "int", "FreeLibrary", "ptr", $gh_DllCallBack_hUser32)
                If @error Then Return SetError(2, 0, False)
                If $aTmp[0] = 0 Then Return SetError(2, 0, False)
                $gh_DllCallBack_hUser32 = 0
            EndIf
            Return True
        EndIf
        Return False
    EndIf
    Return True
EndFunc   ;==>_DllCallBackFree

Share this post


Link to post
Share on other sites

Posted

Impressive, piccaso! Though it still preferable have this ability inbuilt, with this solution looks not utterly need!

Share this post


Link to post
Share on other sites

Posted

Impressive, piccaso! Though it still preferable have this ability inbuilt, with this solution looks not utterly need!

A plus of having it in-built is that it could be coded to avoid the issue of having UDFs register their own code for OnAutoItExit and/or dealing with multiple OnAutoItExit functions.

-brendan

Share this post


Link to post
Share on other sites

Posted

i dont know if it ever gets official but here is it in udf form...

Revorked my code a bit to make it work with your UDF's as a sample. Nicely done @picaso, even if it newer becomes official..:whistle:

#region - Win32EnumProcs
Global $cb3 ;Has to be global to be able to free data. 
;         Used by *proc samples so placed in the midle of the code
Func _EnumWindowStationsProc($hWnd, $msg, $wParam, $lParam)
    Local $station, $sOut
    $Param = DllStructCreate("ptr;ptr", $lParam)
    $station = DllStructCreate("char[256]", DllStructGetData($Param, 1))
    $sOut = DllStructGetData($station, 1) & @CRLF
    ConsoleWrite("_EnumWindowStationsProc: $sOut:=" & $sOut & @CRLF)
    Return 1
EndFunc   ;==>_EnumWindowStationsProc

Func _EnumWindowsProc($hWnd, $msg, $wParam, $lParam)
    Local $tStruct, $sOut
    $tStruct = DllStructCreate("int;int", $lParam)
    $sOut  = WinGetTitle(HWND(DllStructGetData($tStruct, 1)))
    ; NOTE: Calling external global variable
    Assert($cb3 <> 0)
    Assert(HWND(DllStructGetData($tStruct, 1)) <> 0 )
    DllCall("user32.dll", "int", "EnumChildWindows", "hwnd", HWND(DllStructGetData($tStruct, 1)), "ptr", $cb3, "long", 0 )
    AssertNoErr()
    ConsoleWrite("_EnumWindowsProc: $hWnd:=" & DllStructGetData($tStruct, 1) & ", $lParam:=" & DllStructGetData($tStruct, 2) & ", $sOut:=" & $sOut & @CRLF)

  Return 1
EndFunc
Func _EnumChildWindowsProc($hWnd, $msg, $wParam, $lParam)
    Local $tStruct, $sOut
    $tStruct = DllStructCreate("int;int", $lParam)
    ConsoleWrite("_EnumChildWindowsProc: $hWnd:=" & HWND(DllStructGetData($tStruct, 1)) & ", $lParm:=" & HWND(DllStructGetData($tStruct, 2)) & @CRLF )
    Return 1
EndFunc
#endregion - Win32EnumProcs

Func Main2()
    HotKeySet("{ESC}", "Terminate2")
    Local $cb1, $cb2
    Local $hWndDumy = GUICreate("")
    Local $WM_USER = 0x400
    ; Get stub pointer, Register message, call the api
    $cb1 = _DllCallBackAlloc( 2, $hWndDumy, $WM_USER +1)
    If AssertNoErr() Then 
        GUIRegisterMsg($WM_USER +1, "_EnumWindowStationsProc")
        DllCall("user32.dll", "int", "EnumWindowStations", "ptr", $cb1, "long", 0)  

            ;
            ; Get stub pointer, Register message, call the api
            ; Used by $cb2 To enumerate childwindows    
            $cb3 = _DllCallBackAlloc( 2,$hWndDumy, $WM_USER +3)
            dbg("$cb3:=" & $cb3)
            AssertNoErr()
            GUIRegisterMsg($WM_USER +3, "_EnumChildWindowsProc")    
            AssertNoErr()
            ;   
            ; Get stub pointer, Register message, call the api
            Local $cb2 = _DllCallBackAlloc( 2, $hWndDumy, $WM_USER +2)
            AssertNoErr()
            GUIRegisterMsg($WM_USER +2, "_EnumWindowsProc")
            AssertNoErr()
            DllCall("user32.dll", "int", "EnumWindows", "ptr", $cb2, "long", 0)
            AssertNoErr()
        ; The normal application loop
        Do 
            Switch GuiGetMsg()
                Case -3
                    ExitLoop
                Case 0
                    Sleep(100)
                Case $WM_User + 1, $WM_User + 2, $WM_User + 3
                    ConsoleWrite("Unexpected??: $WM_User + [1-3]" & @CRLF)
            EndSwitch
        Until $gTerminate2
    EndIf
    ; Clean up. NOTE: This is on the global heap so it is imperative to clean up!
    _DllCallBackFree($cb1)
    _DllCallBackFree($cb3)
    _DllCallBackFree($cb2)
EndFunc
Global $gTerminate2 = false
Func Terminate2()
    $gTerminate2 = True
EndFuncFunc Assert($bool, $line=@ScriptLineNumber, $err=@error, $ext=@extended)
    If NOT $bool then ConsoleWrite("(" & $line & ") := (" & $err & ")(" & $ext &") Assert failure: " & @CRLF)
    return $bool
EndFunc
Func AssertNoErr($line=@ScriptLineNumber, $err=@error, $ext=@extended)
    If $err <> 0 then ConsoleWrite("(" & $line & ") := (" & $err & ")(" & $ext &") AssertNoErr" & @CRLF)
    return ($err = 0)
EndFunc
Global $gTrace = True
Func Trace($msg="", $line=@ScriptLineNumber, $err=@error, $ext=@extended)
    If $gTrace then ConsoleWrite("(" & $line & ") := (" & $err & ")(" & $ext &") TRACE: " &  $msg & @CRLF)
EndFunc
Global $gDbg = True
Func dbg($msg="", $line=@ScriptLineNumber, $err=@error, $ext=@extended)
    If $gDbg then ConsoleWrite("(" & $line & ") := (" & $err & ")(" & $ext &") TRACE: " &  $msg & @CRLF)
EndFunc
; ========================================
Main2()

Share this post


Link to post
Share on other sites

Posted

CallBack can be used in Rich Edit Controls for

EM_STREAMIN Message

EM_STREAMOUT Message

In EDITSTREAM structure is EDITSTREAMCALLBACK

See MSDN IN and MSDN OUT

With this we can save/load RTF file from/to RichEdit control

Share this post


Link to post
Share on other sites

Posted

i read some articles about the global heap and i think explicit deallocation isnt necessary.

Now you're probably wondering if there is any difference between the local and global functions themselves. Well, the answer is no, they are now the same. In fact, they are interchangeable. Memory allocated via a call to LocalAlloc can be reallocated with GlobalReAlloc and then locked by LocalLock. The following table lists the global and local functions now available in Win32.

Other memory management routines such as GlobalAlloc, GlobalLock, GlobalFree, malloc, free, new, and delete, have all been changed to use VirtualAlloc and VirtualFree at a low level.

... and so on.

so if i get this right GloballAlloc is just short for Allocating executable memory on local heap and the name is just a win16 leftover :whistle:

I would prefer an this as an built-in feature too but just because the detour over windows messaging could be avoided.

But this is probably pretty tricky since the os fires callbacks whenever it wants to and might interrupt autoit in doing something...

and there are only a few situations where the windows message detour variant doesn't work (like crt's atexit)

It has been requested a couple of times and never happened so i guess it will stay that way.

Probably because it isn't essential...

thanks for the sample uten, i'll add it to my collection :lmao:

Share this post


Link to post
Share on other sites

Posted (edited)

simplified it a bit more, now usage can be as easy as this:

#include "DllCallBack.au3"

; Create Stub
$hStub_EnumWindows = _DllCallBack ("_EnumWindowsProc", "hwnd;ptr")

; Call EnumWindows
DllCall("user32.dll", "int", "EnumWindows", "ptr", $hStub_EnumWindows, "long", 0)

; Callback Procedure
Func _EnumWindowsProc($hWnd, $lParam)
    $hWnd = HWnd($hWnd)
    ConsoleWrite($hWnd & " -> " & WinGetTitle($hWnd) & @CRLF)
    Return 1
EndFunc   ;==>_EnumWindowsProc

; Free Stub
_DllCallBack_Free ($hStub_EnumWindows)

more examples in the archive

moved here

Edited by piccaso

Share this post


Link to post
Share on other sites
This topic is now closed to further replies.
Sign in to follow this  
Followers 0