Jump to content

DllStructCreate & DllStructGetPtr


Recommended Posts

Trying to understand how DllStructCreate & DllStructGetPtr work.
why can't retrieve data from client side using DllStructGetPtr?

Main.au3

Local $otext = "How are You?"

Local $o_Buffer = "char[" & StringLen($otext) + 1 & "]"
Local $o_Struct = DllStructCreate($o_Buffer)
Local $o_Ptr = DllStructGetPtr($o_Struct)

ConsoleWrite("GetSize-> " & DllStructGetSize($o_Struct) & @CRLF)
ConsoleWrite("Ptr-> " & $o_Ptr & @CRLF)

;~ SetData
DllStructSetData($o_Struct, 1, $otext)
ConsoleWrite("GetData-> " & DllStructGetData($o_Struct, 1) & @CRLF)

; Start client
RunWait( @AutoItExe & " /AutoIt3ExecuteScript " & '"Client.au3" ' & $o_Buffer & " " & $o_Ptr)

ConsoleWrite(@CRLF)
ConsoleWrite("> ================ "& @CRLF)
;~ to check if convert Ptr to sting and again convert back to Ptr
Local $to_String = String($o_Ptr)
Local $to_Ptr = Ptr($to_String)

Local $to_struct = DllStructCreate($o_Buffer, $to_Ptr)
ConsoleWrite("GetSize-> " & DllStructGetSize($to_struct) & @CRLF)
ConsoleWrite("Ptr-> " & DllStructGetPtr($to_struct) & @CRLF)
ConsoleWrite("GetData-> " & DllStructGetData($to_struct, 1) & @CRLF)

$to_struct = 0
$o_Struct = 0

Client.au3

#include <Array.au3>
MsgBox( 0, "Client", "Client Started" )
;_ArrayDisplay($CmdLine)

Local $t_Buffer = $CmdLine[1]
Local $t_Ptr = Ptr($CmdLine[2])

Local $t_struct = DllStructCreate($t_Buffer, $t_Ptr)
If @error Then
   MsgBox(0, "", "Error in DllStructCreate, Code: " & @error)
   Exit
EndIf

MsgBox(0, "Client", "GetSize-> " & DllStructGetSize($t_struct))
MsgBox(0, "Client", "Ptr-> " & DllStructGetPtr($t_struct))

;~ failed to get Data when DllStructGetPtr showing correct value
MsgBox(0, "Client", "GetData-> " &  DllStructGetData($t_struct, 1))

$t_struct = 0
MsgBox( 0, "Client", "client End" )

 

Edited by jugador
Link to post
Share on other sites

If only it were that simple...;) You have to keep in mind that each process is allocated its own (virtual) memory region, with its own, unique, process-specific base address. Imagine what would happen if any process were able to read/write/erase any region in physical memory that might be concurrently used by several other processes...:blink:

Instead you need to designate a specific memory region as shared between two (or more) processes. There are various examples on the forum (search for IPC (inter-process communication), memory-mapped files, etc). My own HighMem UDF, for example,  provides some (heavily-annotated) functions to map/unmap process-external memory, convert absolute/relative offsets, and mutex handling to avoid race conditions. But if you just need to exchange a few variables between scripts, there are easier IPC options such as MailSlot to consider.

 

 

 

Edited by RTFC
Link to post
Share on other sites

The example of _WinAPI_CreateFileMapping in the help file contains all the code you need.

Study this Microsoft example, if you need more information about this memory sharing technique and the required API functions.

Link to post
Share on other sites

@LarsJ Thanks

with _WinAPI_CreateFileMapping technique can I pass

array 

Local $arry[][] = [["Blackberry",3],["Strawberry",4]]

or object

Local $oHttpObj = ObjCreate('WinHttp.WinHttpRequest.5.1')

or this 

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")

__Example1()
Func __Example1()
    Local $o_FakeObj
    Local $tFakeObj
    $o_FakeObj = _ObjectFromTag("__MyInterface_", "Message hresult(bstr)", $tFakeObj)
    $o_FakeObj.Message("Test message.....")
    If IsObj($o_FakeObj) Then $o_FakeObj = 0
EndFunc

Func __MyInterface_QueryInterface($pSelf, $pRIID, $pObj)
    Local $tStruct = DllStructCreate("ptr", $pObj)
    DllStructSetData($tStruct, 1, $pSelf)
    Return 0 ; $S_OK
EndFunc
Func __MyInterface_AddRef($pSelf)
    Return 1
EndFunc
Func __MyInterface_Release($pSelf)
    Return 1
EndFunc
Func __MyInterface_Message($pSelf, $pString)
    Local $o_Data = DllStructGetData(DllStructCreate("wchar[" & _
                    DllStructGetData(DllStructCreate("dword", $pString - 4), 1) / 2 & "]", $pString), 1)

    MsgBox(0, 'FakeObj', $o_Data)
    Return 0 ; $S_OK
EndFunc

; #FUNCTION# =============================================================================
; Name...........: _ObjectFromTag
; ========================================================================================
Func _ObjectFromTag($sFunctionPrefix, $tagInterface, ByRef $tInterface, $fPrint = False, $bIsUnknown = Default, $sIID = "{00000000-0000-0000-C000-000000000046}") ; last param is IID_IUnknown by default
    If $bIsUnknown = Default Then $bIsUnknown = True
    Local $sInterface = $tagInterface ; copy interface description
    Local $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
            "AddRef dword();" & _
            "Release dword();"
    ; Adding IUnknown methods
    If $bIsUnknown Then $tagInterface = $tagIUnknown & $tagInterface
    ; Below line is really simple even though it looks super complex. It's just written weird to fit in one line, not to steal your attention
    Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3)
    Local $iUbound = UBound($aMethods)
    Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback
    ; Allocation
    $tInterface = DllStructCreate("int RefCount;int Size;ptr Object;ptr Methods[" & $iUbound & "];int_ptr Callbacks[" & $iUbound & "];ulong_ptr Slots[16]") ; 16 pointer sized elements more to create space for possible private props
    If @error Then Return SetError(1, 0, 0)
    For $i = 0 To $iUbound - 1
        $aSplit = StringSplit($aMethods[$i], "|", 2)
        If UBound($aSplit) <> 2 Then ReDim $aSplit[2]
        $sNamePart = $aSplit[0]
        $sTagPart = $aSplit[1]
        $sMethod = $sFunctionPrefix & $sNamePart
        If $fPrint Then
            Local $iPar = StringInStr($sTagPart, ";", 2), $t
            If $iPar Then
                $t = "Ret: " & StringLeft($sTagPart, $iPar - 1) & "  " & _
                        "Par: " & StringRight($sTagPart, StringLen($sTagPart) - $iPar)
            Else
                $t = "Ret: " & $sTagPart
            EndIf
            Local $s = "Func " & $sMethod & _
                    "( $pSelf ) ; " & $t & @CRLF & _
                    "EndFunc" & @CRLF
            ConsoleWrite($s)
        EndIf
        $aTagPart = StringSplit($sTagPart, ";", 2)
        $sRet = $aTagPart[0]
        $sParams = StringReplace($sTagPart, $sRet, "", 1)
        $sParams = "ptr" & $sParams
        $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams)
        If @error Then
            ConsoleWrite('! ' & @error & ' ' & $sMethod & @CRLF & @CRLF)
        EndIf

        DllStructSetData($tInterface, "Methods", DllCallbackGetPtr($hCallback), $i + 1) ; save callback pointer
        DllStructSetData($tInterface, "Callbacks", $hCallback, $i + 1) ; save callback handle
    Next
    DllStructSetData($tInterface, "RefCount", 1) ; initial ref count is 1
    DllStructSetData($tInterface, "Size", $iUbound) ; number of interface methods
    DllStructSetData($tInterface, "Object", DllStructGetPtr($tInterface, "Methods")) ; Interface method pointers
    Return ObjCreateInterface(DllStructGetPtr($tInterface, "Object"), $sIID, $sInterface, $bIsUnknown) ; pointer that's wrapped into object
EndFunc   ;==>ObjectFromTag

Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc   ;==>_ErrFunc

@LarsJ  can you show how to write DllStructCreate for the above array and object.

Edited by jugador
Link to post
Share on other sites

For the examples above, you must use ROT objects.

This is a server/client implementation of your FakeObj.

Server.au3: 

#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

#include "IRunningObjectTable.au3"

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")

Example()

Func Example()
  Local $tFakeObj, $oFakeObj
  $oFakeObj = _ObjectFromTag("__MyInterface_", "Message hresult(bstr)", $tFakeObj)
  $oFakeObj.Message("Test message from Server .....")

  ; Create a default ROT-object (Dictionary object)
  Local $sDataTransferObject, $oDataTransferObject
  $oDataTransferObject = ROT_CreateDefaultObject( $sDataTransferObject )

  ; Create Dictionary object
  Local $oDict = ObjCreate( "Scripting.Dictionary" )
  $oDict.Item( "$oFakeObj" ) = $oFakeObj

  ; Add Dictionary object to ROT-object
  $oDataTransferObject.Add( "$oDict", $oDict )

  ; Start the client script in a new process
  RunWait( @AutoItExe & " /AutoIt3ExecuteScript Client.au3" & " " & $sDataTransferObject )
EndFunc

Func __MyInterface_QueryInterface($pSelf, $pRIID, $pObj)
  Local $tStruct = DllStructCreate("ptr", $pObj)
  DllStructSetData($tStruct, 1, $pSelf)
  Return 0 ; $S_OK
  #forceref $pRIID
EndFunc
Func __MyInterface_AddRef($pSelf)
  Return 1
  #forceref $pSelf
EndFunc
Func __MyInterface_Release($pSelf)
  Return 1
  #forceref $pSelf
EndFunc
Func __MyInterface_Message($pSelf, $pString)
  Local $o_Data = DllStructGetData(DllStructCreate("wchar[" & _
                  DllStructGetData(DllStructCreate("dword", $pString - 4), 1) / 2 & "]", $pString), 1)

  MsgBox(0, 'FakeObj', $o_Data)
  Return 0 ; $S_OK
  #forceref $pSelf
EndFunc

; #FUNCTION# =============================================================================
; Name...........: _ObjectFromTag
; ========================================================================================
Func _ObjectFromTag($sFunctionPrefix, $tagInterface, ByRef $tInterface, $fPrint = False, $bIsUnknown = Default, $sIID = "{00000000-0000-0000-C000-000000000046}") ; last param is IID_IUnknown by default
    If $bIsUnknown = Default Then $bIsUnknown = True
    Local $sInterface = $tagInterface ; copy interface description
    Local $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
            "AddRef dword();" & _
            "Release dword();"
    ; Adding IUnknown methods
    If $bIsUnknown Then $tagInterface = $tagIUnknown & $tagInterface
    ; Below line is really simple even though it looks super complex. It's just written weird to fit in one line, not to steal your attention
    Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3)
    Local $iUbound = UBound($aMethods)
    Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback
    ; Allocation
    $tInterface = DllStructCreate("int RefCount;int Size;ptr Object;ptr Methods[" & $iUbound & "];int_ptr Callbacks[" & $iUbound & "];ulong_ptr Slots[16]") ; 16 pointer sized elements more to create space for possible private props
    If @error Then Return SetError(1, 0, 0)
    For $i = 0 To $iUbound - 1
        $aSplit = StringSplit($aMethods[$i], "|", 2)
        If UBound($aSplit) <> 2 Then ReDim $aSplit[2]
        $sNamePart = $aSplit[0]
        $sTagPart = $aSplit[1]
        $sMethod = $sFunctionPrefix & $sNamePart
        If $fPrint Then
            Local $iPar = StringInStr($sTagPart, ";", 2), $t
            If $iPar Then
                $t = "Ret: " & StringLeft($sTagPart, $iPar - 1) & "  " & _
                        "Par: " & StringRight($sTagPart, StringLen($sTagPart) - $iPar)
            Else
                $t = "Ret: " & $sTagPart
            EndIf
            Local $s = "Func " & $sMethod & _
                    "( $pSelf ) ; " & $t & @CRLF & _
                    "EndFunc" & @CRLF
            ConsoleWrite($s)
        EndIf
        $aTagPart = StringSplit($sTagPart, ";", 2)
        $sRet = $aTagPart[0]
        $sParams = StringReplace($sTagPart, $sRet, "", 1)
        $sParams = "ptr" & $sParams
        $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams)
        If @error Then
            ConsoleWrite('! ' & @error & ' ' & $sMethod & @CRLF & @CRLF)
        EndIf

        DllStructSetData($tInterface, "Methods", DllCallbackGetPtr($hCallback), $i + 1) ; save callback pointer
        DllStructSetData($tInterface, "Callbacks", $hCallback, $i + 1) ; save callback handle
    Next
    DllStructSetData($tInterface, "RefCount", 1) ; initial ref count is 1
    DllStructSetData($tInterface, "Size", $iUbound) ; number of interface methods
    DllStructSetData($tInterface, "Object", DllStructGetPtr($tInterface, "Methods")) ; Interface method pointers
    Return ObjCreateInterface(DllStructGetPtr($tInterface, "Object"), $sIID, $sInterface, $bIsUnknown) ; pointer that's wrapped into object
EndFunc   ;==>ObjectFromTag

Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc   ;==>_ErrFunc

Client.au3: 

#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  ; Get default ROT-object (Dictionary object)
  Local $oDataTransferObject = ObjGet( $CmdLine[1] )

  ; Get Dictionary object from ROT-object
  Local $oDict = $oDataTransferObject.Item( "$oDict" )

  Local $oFakeObj = $oDict.Item( "$oFakeObj" )
  $oFakeObj.Message("Test message from Client .....")
EndFunc

Run Server.au3.

Come on. This is f ... (freezing) cool code. That was fun.

All code in the 7z-file.

FakeObj.7z

You can try the other two examples yourself.

Edited by LarsJ
Link to post
Share on other sites

@LarsJ that's easy using your IRunningObjectTable Interface Udf.

Already tested ScriptControl method to transfer Array & Object from Autoit to Jscript & Vbscript.
but trying to find solution to pass object between two Autoit script.

Thanks for this ROT objects concept.

Edited by jugador
Link to post
Share on other sites

Memory read using Ptr
this for string 
don't know how to pass Object and Array using this technique.
example on Object and Array using this technique from fellow Autoit member would be nice :).

below code(by @Ascer) taken from this thread
https://www.autoitscript.com/forum/topic/192339-reading-memory-double/?do=findComment&comment=1381902

Main.au3

Local $otext = "How are You?"

Local $o_PID = WinGetProcess("AutoIt v3")
Local $o_Buffer = "char[" & StringLen($otext) + 1 & "]"
Local $o_Struct = DllStructCreate($o_Buffer)
Local $o_Ptr = DllStructGetPtr($o_Struct)

;~ SetData
DllStructSetData($o_Struct, 1, $otext)

; Start client
RunWait( @AutoItExe & " /AutoIt3ExecuteScript " & '"Client.au3" ' & $o_PID & " " & $o_Buffer & " " & $o_Ptr)
$o_Struct = 0

Client.au3

MsgBox( 0, "Client", "Client.... Started" )

Local $t_Pid = Number($CmdLine[1])
Local $t_Buffer = $CmdLine[2]
Local $t_Address = Number($CmdLine[3])

Local $hHandle = DllCall("kernel32.dll", 'int', 'OpenProcess', 'int', 0x1F0FFF, 'int', 1, 'int', $t_Pid)
If @error Then
    MsgBox( 0, "Client", "+++ Failed to open process memory for FULL_ACCESS. Error is " & @error )
    Exit
EndIf

Local $sStruct = DllStructCreate($t_Buffer)
If @error Then
    MsgBox( 0, "Client", "+++ Failed to create $sStruct. Error is " & @error )
    Exit
EndIf

DllCall("kernel32.dll", 'int', 'ReadProcessMemory', 'int', $hHandle[0], 'int', $t_Address, 'ptr', DllStructGetPtr($sStruct), 'int', DllStructGetSize($sStruct), 'int', '')
If @error Then
    MsgBox( 0, "Client", "+++ Failed to Read Process Memory. Error is " & @error )
    Exit
EndIf

Local $vRet = DllStructGetData($sStruct, 1)
If @error Then
    MsgBox( 0, "Client", "+++ Failed to Get data from $sStruct. Error is " & @error )
    Exit
EndIf

MsgBox( 0, "Client", $vRet )
MsgBox( 0, "Client", "Client.... End" )

 

Edited by jugador
Link to post
Share on other sites

This is a new version of the server/client example above based on the FakeObj object. Again, FakeObj is passed to the client through a ROT-object. Here, FakeObj can handle strings, doubles, integers and arrays.

Server.au3: 

#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

#include <Array.au3>

#include "AccVars.au3"
#include "IRunningObjectTable.au3"

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")

Example()

Func Example()
  ConsoleWrite( "Code running on server ..." & @CRLF )
  Local $tFakeObj, $oFakeObj
  $oFakeObj = _ObjectFromTag("__MyInterface_", "Data hresult(variant)", $tFakeObj)
  $oFakeObj.Data("Test string from Server .....")
  $oFakeObj.Data( 123.123 )
  $oFakeObj.Data( 123 )
  Local $aArray = [ 123, 123.123, "Server test string" ]
  $oFakeObj.Data( $aArray )

  ; Create a default ROT-object (Dictionary object)
  Local $sDataTransferObject, $oDataTransferObject
  $oDataTransferObject = ROT_CreateDefaultObject( $sDataTransferObject )

  ; Create Dictionary object
  Local $oDict = ObjCreate( "Scripting.Dictionary" )
  $oDict.Item( "$oFakeObj" ) = $oFakeObj

  ; Add Dictionary object to ROT-object
  $oDataTransferObject.Add( "$oDict", $oDict )

  ; Start the client script in a new process
  ConsoleWrite( @CRLF & "Code running on Client ..." & @CRLF )
  RunWait( @AutoItExe & " /AutoIt3ExecuteScript Client.au3" & " " & $sDataTransferObject )
EndFunc

Func __MyInterface_QueryInterface($pSelf, $pRIID, $pObj)
  Local $tStruct = DllStructCreate("ptr", $pObj)
  DllStructSetData($tStruct, 1, $pSelf)
  Return 0 ; $S_OK
  #forceref $pRIID
EndFunc
Func __MyInterface_AddRef($pSelf)
  Return 1
  #forceref $pSelf
EndFunc
Func __MyInterface_Release($pSelf)
  Return 1
  #forceref $pSelf
EndFunc

Func __MyInterface_Data($pSelf, $pVariant)
  ConsoleWrite( @CRLF & "$pVar = " & $pVariant & @CRLF )
  Local $tVariant = DllStructCreate( $tagVARIANT, $pVariant )
  Local $sAlign1 = "    " & ( @AutoItX64 ? "        " : "" )
  Local $iVariantType = DllStructGetData( $tVariant, "vt" )
  ConsoleWrite( "Type  = 0x" & Hex( $iVariantType, 4 ) & " " & $sAlign1 )
  ; 4 word elements before the data element = 8 bytes
  Local $pData = $pVariant + 8, $tPtr, $pPtr

  Switch $iVariantType
    Case $VT_I4 ; 4 bytes signed integer
      ConsoleWrite( "(VT_I4, 4 bytes signed integer)" & @CRLF )
      ; The data element contains the integer in the 4 first bytes
      Local $tInt = DllStructCreate( "int", $pData )
      Local $iInt = DllStructGetData( $tInt, 1 )
      ConsoleWrite( "data  = " & $iInt & @CRLF )

    Case $VT_R8 ; 8 bytes double
      ConsoleWrite( "(VT_R8, 8 bytes double)" & @CRLF )
      ; The data element contains the double in the 8 first bytes
      ; That's the entire data element on 32 bit and half the data element on 64 bit
      Local $tFlt = DllStructCreate( "double", $pData )
      Local $fFlt = DllStructGetData( $tFlt, 1 )
      ConsoleWrite( "data  = " & $fFlt & @CRLF )

    Case $VT_BSTR ; Basic string
      ConsoleWrite( "(VT_BSTR, basic string)" & @CRLF )
      ; The data element contains a pointer to the BSTR
      $tPtr = DllStructCreate( "ptr", $pData )
      $pPtr = DllStructGetData( $tPtr, 1 )
      ConsoleWrite( "pBSTR = " & $pPtr & " (BSTR pointer)" & @CRLF )
      ; The BSTR must be read with a proper BSTR-function (see Variant.au3)
      Local $sStr = SysReadString( $pPtr )
      ConsoleWrite( "data  = " & $sStr & @CRLF )

    Case $VT_ARRAY + $VT_VARIANT ; Array of variants
      ConsoleWrite( "(VT_ARRAY+VT_VARIANT, array of variants, safearray)" & @CRLF )
      ; The data element contains a pointer to the safearray in the 4/8 first bytes
      $tPtr = DllStructCreate( "ptr", $pData )
      $pPtr = DllStructGetData( $tPtr, 1 )
      ConsoleWrite( "data  = " & $pPtr & " (pointer to safearray)" & @CRLF )

      ; Print safearray data to console
      ConsoleWrite( @CRLF & "Safearray data:" & @CRLF )
      PrintSafeArray1D( $pPtr )

      ; Convert safearray to AutoIt array through internal COM conversions
      ; Display the AutoIt array with _ArrayDisplay( $aArray )
      ConsoleWrite( @CRLF & "Convert safearray to AutoIt array" & @CRLF )
      Local $aArray
      SafeArrayLock( $pPtr )
      AccessVariables02( AccVars_SafeArrayToArray, $pPtr, $aArray )
      SafeArrayUnlock( $pPtr )
      ConsoleWrite( "_ArrayDisplay( <AutoIt array> ) ..." & @CRLF )
      _ArrayDisplay( $aArray )

    Case Else
      ConsoleWrite( "Variant type = " & DllStructGetData( $tVariant, "vt" ) & @CRLF )
      ConsoleWrite( "Unhandled variant type" & @CRLF )
  EndSwitch

  Return 0 ; $S_OK
  #forceref $pSelf
EndFunc

; #FUNCTION# =============================================================================
; Name...........: _ObjectFromTag
; ========================================================================================
Func _ObjectFromTag($sFunctionPrefix, $tagInterface, ByRef $tInterface, $fPrint = False, $bIsUnknown = Default, $sIID = "{00000000-0000-0000-C000-000000000046}") ; last param is IID_IUnknown by default
    If $bIsUnknown = Default Then $bIsUnknown = True
    Local $sInterface = $tagInterface ; copy interface description
    Local $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
            "AddRef dword();" & _
            "Release dword();"
    ; Adding IUnknown methods
    If $bIsUnknown Then $tagInterface = $tagIUnknown & $tagInterface
    ; Below line is really simple even though it looks super complex. It's just written weird to fit in one line, not to steal your attention
    Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3)
    Local $iUbound = UBound($aMethods)
    Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback
    ; Allocation
    $tInterface = DllStructCreate("int RefCount;int Size;ptr Object;ptr Methods[" & $iUbound & "];int_ptr Callbacks[" & $iUbound & "];ulong_ptr Slots[16]") ; 16 pointer sized elements more to create space for possible private props
    If @error Then Return SetError(1, 0, 0)
    For $i = 0 To $iUbound - 1
        $aSplit = StringSplit($aMethods[$i], "|", 2)
        If UBound($aSplit) <> 2 Then ReDim $aSplit[2]
        $sNamePart = $aSplit[0]
        $sTagPart = $aSplit[1]
        $sMethod = $sFunctionPrefix & $sNamePart
        If $fPrint Then
            Local $iPar = StringInStr($sTagPart, ";", 2), $t
            If $iPar Then
                $t = "Ret: " & StringLeft($sTagPart, $iPar - 1) & "  " & _
                        "Par: " & StringRight($sTagPart, StringLen($sTagPart) - $iPar)
            Else
                $t = "Ret: " & $sTagPart
            EndIf
            Local $s = "Func " & $sMethod & _
                    "( $pSelf ) ; " & $t & @CRLF & _
                    "EndFunc" & @CRLF
            ConsoleWrite($s)
        EndIf
        $aTagPart = StringSplit($sTagPart, ";", 2)
        $sRet = $aTagPart[0]
        $sParams = StringReplace($sTagPart, $sRet, "", 1)
        $sParams = "ptr" & $sParams
        $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams)
        If @error Then
            ConsoleWrite('! ' & @error & ' ' & $sMethod & @CRLF & @CRLF)
        EndIf

        DllStructSetData($tInterface, "Methods", DllCallbackGetPtr($hCallback), $i + 1) ; save callback pointer
        DllStructSetData($tInterface, "Callbacks", $hCallback, $i + 1) ; save callback handle
    Next
    DllStructSetData($tInterface, "RefCount", 1) ; initial ref count is 1
    DllStructSetData($tInterface, "Size", $iUbound) ; number of interface methods
    DllStructSetData($tInterface, "Object", DllStructGetPtr($tInterface, "Methods")) ; Interface method pointers
    Return ObjCreateInterface(DllStructGetPtr($tInterface, "Object"), $sIID, $sInterface, $bIsUnknown) ; pointer that's wrapped into object
EndFunc   ;==>ObjectFromTag

Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc   ;==>_ErrFunc

Func PrintSafeArray1D( $pSafeArray )
  If Not IsPtr( $pSafeArray ) Then Return SetError(1,0,0)
  If Not SafeArrayGetDim( $pSafeArray ) = 1 Then Return SetError(2,0,0)
  Local $tSafeArray = DllStructCreate( $tagSAFEARRAY, $pSafeArray )
  Local $iElemSize = DllStructGetData( $tSAFEARRAY, "cbElements" )
  Local $iUBound, $pSafeArrayData, $vt, $data
  SafeArrayGetUBound( $pSafeArray, 1, $iUBound )
  SafeArrayAccessData( $pSafeArray, $pSafeArrayData )
  For $i = 0 To $iUBound
    $vt = DllStructGetData( DllStructCreate( "word", $pSafeArrayData ), 1 )
    Switch $vt
      Case $VT_I4 ; 4 bytes signed integer
        $data = DllStructGetData( DllStructCreate( "int", $pSafeArrayData + 8 ), 1 )
      Case $VT_R8 ; 8 bytes double
        $data = DllStructGetData( DllStructCreate( "double", $pSafeArrayData + 8 ), 1 )
      Case $VT_BSTR ; Basic string
        $data = SysReadString( DllStructGetData( DllStructCreate( "ptr", $pSafeArrayData + 8 ), 1 ) )
      Case Else
        $data = ""
    EndSwitch
    ConsoleWrite( $data & @CRLF )
    $pSafeArrayData += $iElemSize
  Next
  SafeArrayUnaccessData( $pSafeArray )
EndFunc

Func AccVars_SafeArrayToArray( $pvSafeArray, $pArray )
  ; <<<< On function entry the native AutoIt array is converted to a safearray contained in a variant >>>>

  ; --- Get safearray information ---

  ; $pvSafeArray is a variant that contains a pointer
  Local $pSafeArray = DllStructGetData( DllStructCreate( "ptr", $pvSafeArray + 8 ), 1 )

  ; Array type
  Local $iVarType
  SafeArrayGetVartype( $pSafeArray, $iVarType )
  Switch $iVarType
    Case $VT_I2, $VT_I4   ; Signed integers
    Case $VT_R4, $VT_R8   ; 4/8 bytes floats
    Case $VT_BSTR         ; Basic string
    Case $VT_BOOL         ; Boolean
    Case $VT_UI4, $VT_UI8 ; 4/8 bytes unsigned integers
    Case $VT_VARIANT      ; Variant
    Case Else
      Return SetError(1,0,0)
  EndSwitch

  ; --- Set $pArray to match an array ---

  ; Set vt element to $VT_ARRAY + $iVarType
  DllStructSetData( DllStructCreate( "word", $pArray ), 1, $VT_ARRAY + $iVarType )

  ; Set data element to safearray pointer
  DllStructSetData( DllStructCreate( "ptr", $pArray + 8 ), 1, $pSafeArray )

  ; <<<< On function exit the safearray contained in a variant is converted to a native AutoIt array >>>>
EndFunc

Client.au3: 

#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  ; Get default ROT-object (Dictionary object)
  Local $oDataTransferObject = ObjGet( $CmdLine[1] )

  ; Get Dictionary object from ROT-object
  Local $oDict = $oDataTransferObject.Item( "$oDict" )

  Local $oFakeObj = $oDict.Item( "$oFakeObj" )
  $oFakeObj.Data("Test string from Client .....")
  $oFakeObj.Data( 123.123 )
  $oFakeObj.Data( 123 )
  Local $aArray = [ 123, 123.123, "Client test string" ]
  $oFakeObj.Data( $aArray )
EndFunc

Run Server.au3.

Output in SciTE console: 

Code running on server ...

$pVar = 0x000000000045ED30
Type  = 0x0008             (VT_BSTR, basic string)
pBSTR = 0x0000000000928EE8 (BSTR pointer)
data  = Test string from Server .....

$pVar = 0x000000000045ED30
Type  = 0x0005             (VT_R8, 8 bytes double)
data  = 123.123

$pVar = 0x000000000045ED30
Type  = 0x0003             (VT_I4, 4 bytes signed integer)
data  = 123

$pVar = 0x000000000045ED30
Type  = 0x200C             (VT_ARRAY+VT_VARIANT, array of variants, safearray)
data  = 0x000000000095C7B0 (pointer to safearray)

Safearray data:
123
123.123
Server test string

Convert safearray to AutoIt array
_ArrayDisplay( <AutoIt array> ) ...

Code running on Client ...

$pVar = 0x000000000045E520
Type  = 0x0008             (VT_BSTR, basic string)
pBSTR = 0x0000000000926908 (BSTR pointer)
data  = Test string from Client .....

$pVar = 0x000000000045E520
Type  = 0x0005             (VT_R8, 8 bytes double)
data  = 123.123

$pVar = 0x000000000045E520
Type  = 0x0003             (VT_I4, 4 bytes signed integer)
data  = 123

$pVar = 0x000000000045E520
Type  = 0x200C             (VT_ARRAY+VT_VARIANT, array of variants, safearray)
data  = 0x000000000095E8F0 (pointer to safearray)

Safearray data:
123
123.123
Client test string

Convert safearray to AutoIt array
_ArrayDisplay( <AutoIt array> ) ...

All code in the 7z-file.

FakeObj2.7z

Link to post
Share on other sites

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