Jump to content

Recommended Posts

Posted

Right now I'm working on a Task Scheduler UDF.
There are a few objects but a myriad of properties to set. To keep the code small I would like to pass an array with propertynames and values to a function that sets those properties.
Unfortunately it doesn't work. I tried:

Global $aRegistrationInfo[] = ['Author="water"', 'Description="Test-TaskPropertiesSet"']
__TS_TaskPropertiesSet($oRegistrationInfo, $aRegistrationInfo)
...

Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp, $vResult
    #forceref $oObject, $vResult
    If IsArray($aProperties) Then
        For $i = 0 To UBound($aProperties) - 1
            $aTemp = StringSplit($aProperties[$i], "=", $STR_NOCOUNT)
            If @error Then ContinueLoop
ConsoleWrite("$oObject." & $aTemp[0] & "=" & $aTemp[1] & @CRLF)
            $vResult = Execute("$oObject." & $aTemp[0] & "=" & $aTemp[1])
ConsoleWrite($vResult & @CRLF)
            If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

what I get is:

  Quote

$oObject.Author="Thomas"
False
$oObject.Description="Test-TaskPropertiesSet"
False

Expand  

It seems Execute does a comparison and not an assignment.

Any ideas how I can do an assignment?

Thanks in advance!

My UDFs and Tutorials:

  Reveal hidden contents

 

Posted

is this what you are looking for?

  Reveal hidden contents

 

Posted (edited)

Nearly ;)

If works for the Scripting.Dictionary because this object allows to access the properties by name. Unfortunately this is not true for the Task Scheduler object :(
Here is a full "working" example:

#include <StringConstants.au3>

Global $__oTS_Error = ObjEvent("AutoIt.Error", "__TS_ErrorHandler")
Global $oService = ObjCreate("Schedule.Service")
Global $oTaskDefinition = $oService.NewTask(0)
; Global $oRegistrationInfo = $oTaskDefinition.RegistrationInfo() ; ==> Doesn't work. USE ONE OF THIS STATEMENTS
Global $oRegistrationInfo = ObjCreate("Scripting.Dictionary")     ; ==> Works.        USE ONE OF THIS STATEMENTS
Global $aProperties[] = ['Author="water"']
__TS_TaskPropertiesSet($oRegistrationInfo, $aProperties)
Exit

Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp, $o_obj
    #forceref $oObject
    If IsArray($aProperties) Then
        For $i = 0 To UBound($aProperties) - 1
            $aTemp = StringSplit($aProperties[$i], "=", $STR_NOCOUNT)
            If @error Then ContinueLoop
            ConsoleWrite("$oObject." & $aTemp[0] & "=" & $aTemp[1] & @CRLF)
            $o_obj = Eval("oObject")
            $o_obj($aTemp[0]) = $aTemp[1] ;Execute("$oObject." & $aTemp[0] & "=" & $aTemp[1])
            ConsoleWrite($oObject($aTemp[0]) & @CRLF)
            If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

Func __TS_ErrorHandler()
    Local $bHexNumber = Hex($__oTS_Error.number, 8)
    Local $sError = "COM Error Encountered in " & @ScriptName & @CRLF & _
            "@AutoItVersion = " & @AutoItVersion & @CRLF & _
            "@AutoItX64 = " & @AutoItX64 & @CRLF & _
            "@Compiled = " & @Compiled & @CRLF & _
            "@OSArch = " & @OSArch & @CRLF & _
            "@OSVersion = " & @OSVersion & @CRLF & _
            "Scriptline = " & $__oTS_Error.scriptline & @CRLF & _
            "NumberHex = " & $bHexNumber & @CRLF & _
            "Number = " & $__oTS_Error.number & @CRLF & _
            "WinDescription = " & StringStripWS($__oTS_Error.WinDescription, 2) & @CRLF & _
            "Description = " & StringStripWS($__oTS_Error.Description, 2) & @CRLF & _
            "Source = " & $__oTS_Error.Source & @CRLF & _
            "HelpFile = " & $__oTS_Error.HelpFile & @CRLF & _
            "HelpContext = " & $__oTS_Error.HelpContext & @CRLF & _
            "LastDllError = " & $__oTS_Error.LastDllError
        MsgBox(64, "TS UDF - Debug Info", $sError)
EndFunc   ;==>__TS_ErrorHandler

Scripting.Dictionary allows

$oRegistrationInfo("Author") = "xy"
ConsoleWrite($oRegistrationInfo("Author") & @CRLF)
ConsoleWrite($oRegistrationInfo.Item("Author") & @CRLF)

Which isn't possible with the Task Scheduler object.

With Scripting.Dictionary I can even use a variable to store the property name - hence the __TS_PropertySet function is not needed at all for this object.

Edited by water

My UDFs and Tutorials:

  Reveal hidden contents

 

Posted (edited)

yeah I see no way around autoits limitations with that how about we switch tactics?

 

I see the interface has put and get xml

lets get an empty xml template from XmlText change what we want then set it back

  Reveal hidden contents

 

Edited by Bilgus
Posted

The intended purpose of _TS_TaskPropertiesSet was to allow the user or a wrapper function to pass an array with all the needed properties to create the task.
The function I had in mind would have done this with just a few lines of code.
Now I need to implement property for property and hence will get much longer code. The advantage with this approach is, that it is easy to implement much better error checking. As I have written in the main thread Windows Task Scheduler does not deliver helpful error information.
I will stick with the COM approach because to reliable handle XML I think we would need to use one of the XML UDFs. StringReplace might lead to undesired errors which would be hard to debug too.

My UDFs and Tutorials:

  Reveal hidden contents

 

Posted

or just create an empty XML with your desired items and make it so string replace has very concrete items to replace..

I get it though it kinda sucks that Execute doesn't allow you to set the value..

 

Posted
  On 8/31/2019 at 9:15 AM, water said:

It seems Execute does a comparison and not an assignment.

Expand  

Interesting problem...
Already faced such oddity before, but then I gave up...

an even more eccentricity is that if you use the += operator instead of just = inside the Execute it should be forced an assignment indstead of a comparison, but it doesn't works either

I have modified a bit your script just to have a "runnable" script using the "IE browser" as a testing "object" so to allow an easy testing 'environment' to make experiments...

#include <ie.au3>

Global $oRegistrationInfo = _IECreate() ; just to have a "guinea pig" to experiment with
Global $aRegistrationInfo[] = ['Left=100', 'Width=450']

__TS_TaskPropertiesSet($oRegistrationInfo, $aRegistrationInfo)

MsgBox(0, "Info", "Press OK to exit")

$oRegistrationInfo.quit


Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp, $vResult
    #forceref $oObject, $vResult
    If IsArray($aProperties) Then

        For $i = 0 To UBound($aProperties) - 1
            $aTemp = StringSplit($aProperties[$i], "=", 2) ; 2 -> $STR_NOCOUNT)
            If @error Then ContinueLoop

            ConsoleWrite("Command: $oObject." & $aTemp[0] & " += " & $aTemp[1] & @CRLF)
            $vResult = Execute("$oObject." & $aTemp[0] & " += " & $aTemp[1])
            ConsoleWrite("Result : " & $vResult & @CRLF)
            ; If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

It would be interesting to find a solution or even just a workaround. I hope someone "smarter" than me will jump in here, and can find it :)

 

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Posted

CallByName looks very promising :) Unfortunately this function isn't provided by AutoIt :(

My UDFs and Tutorials:

  Reveal hidden contents

 

Posted

no if I can find the actual header I can probably build an interface for it but being that its a vb built-in its kinda hard to find documentation other than the call semantics

Posted (edited)

All I managed to do reliably with the callbyname function is crash AutoIt

However I did manage to make call by name work

It's not going to work for you I doubt because its 600 lines of code but hey I'll post it anyways

  Reveal hidden contents

 

So basically I recreated the method by which autoit calls members (well any com program really)

its definitely beta code and it's barely tested HAVE FUN :)

 

Edited by Bilgus
Posted

Very interesting code :)

For the time being I will hand-code what I need in pure Autoit so I'm still able to read and understand it in a few weeks or later ;) 

My UDFs and Tutorials:

  Reveal hidden contents

 

Posted
  On 9/5/2019 at 5:18 AM, Bilgus said:

.... However I did manage to make call by name work ...

Expand  

... "mamma mia" ! ... :wacko:

sorry, how can I use this to make my little test script that I posted above work. Thanks

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Posted
  On 9/2/2019 at 2:36 PM, Chimp said:

Interesting problem...
Already faced such oddity before, but then I gave up...

an even more eccentricity is that if you use the += operator instead of just = inside the Execute it should be forced an assignment indstead of a comparison, but it doesn't works either

I have modified a bit your script just to have a "runnable" script using the "IE browser" as a testing "object" so to allow an easy testing 'environment' to make experiments...

#include <ie.au3>

Global $oRegistrationInfo = _IECreate() ; just to have a "guinea pig" to experiment with
Global $aRegistrationInfo[] = ['Left=100', 'Width=450']

__TS_TaskPropertiesSet($oRegistrationInfo, $aRegistrationInfo)

MsgBox(0, "Info", "Press OK to exit")

$oRegistrationInfo.quit


Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp, $vResult
    #forceref $oObject, $vResult
    If IsArray($aProperties) Then

        For $i = 0 To UBound($aProperties) - 1
            $aTemp = StringSplit($aProperties[$i], "=", 2) ; 2 -> $STR_NOCOUNT)
            If @error Then ContinueLoop

            ConsoleWrite("Command: $oObject." & $aTemp[0] & " += " & $aTemp[1] & @CRLF)
            $vResult = Execute("$oObject." & $aTemp[0] & " += " & $aTemp[1])
            ConsoleWrite("Result : " & $vResult & @CRLF)
            ; If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

It would be interesting to find a solution or even just a workaround. I hope someone "smarter" than me will jump in here, and can find it :)

 

Expand  

This shouldn't be a problem if you do it by calling Invoke on object yourself. The problem could be converting arguments from AutoIt type to COM type, which AutoIt does internally for you when working with objects the usual way. Anyway for something as simple as this you could do it like this

#include <ie.au3>

; Some constants used in code
Const $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
        "AddRef dword();" & _
        "Release dword();"

Const $tagIDispatch = $tagIUnknown & _
        "GetTypeInfoCount hresult(dword*);" & _
        "GetTypeInfo hresult(dword;dword;ptr*);" & _
        "GetIDsOfNames hresult(struct*;struct*;dword;dword;struct*);" & _
        "Invoke hresult(uint;struct*;dword;word;struct*;struct*;ptr;uint*);"

Const $DISPID_PROPERTYPUT = -3
Const $DISPATCH_PROPERTYPUT = 4
Const $LOCALE_SYSTEM_DEFAULT = 0x800
Const $tIID_NULL = DllStructCreate("byte[16]")
Const $tagDISPPARAMS = "ptr rgvarg;ptr rgdispidNamedArgs;uint cArgs;uint cNamedArgs;"
Const $VT_I4 = 3
Const $tVARIANT = "word vt;word r1;word r2;word r3;ptr data; ptr"
Const $sIID_IDispatch = "{00020400-0000-0000-C000-000000000046}"




$sYourProperty = "Width"
$iDesiredValue = 100

$oRegistrationInfo = _IECreate()  ; just to have a "guinea pig" to experiment with

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Superposed object on top of the original one
$oRegistrationInfoMy = ObjCreateInterface($oRegistrationInfo, $sIID_IDispatch, $tagIDispatch, False)

; Collect ID number of the function/property/method, whatever
$tDisp = DllStructCreate("dword")
$tName = DllStructCreate("ptr")
$tN = DllStructCreate("wchar[" & StringLen($sYourProperty) + 1 & "]")
DllStructSetData($tN, 1, $sYourProperty)
DllStructSetData($tName, 1, DllStructGetPtr($tN))

$oRegistrationInfoMy.GetIDsOfNames($tIID_NULL, $tName, 1, $LOCALE_SYSTEM_DEFAULT, $tDisp)
; Tadaaa!
$iDispId = DllStructGetData($tDisp, 1)


; Now build disp parameters
$tDISPPARAMS = DllStructCreate($tagDISPPARAMS)
$tDISPPARAMS.cNamedArgs = 1
$tDispidNamed = DllStructCreate("uint")
DllStructSetData($tDispidNamed, 1, $DISPID_PROPERTYPUT)
$tDISPPARAMS.rgdispidNamedArgs = DllStructGetPtr($tDispidNamed)
$tDISPPARAMS.cArgs = 1
$tVar = DllStructCreate($tVARIANT)
$tDISPPARAMS.rgvarg = DllStructGetPtr($tVar)

; Set desired value
$tVar.vt = $VT_I4
$tVar.data = $iDesiredValue

; And call it
$iRet = $oRegistrationInfoMy.Invoke($iDispId, $tIID_NULL, 0x800, $DISPATCH_PROPERTYPUT, $tDISPPARAMS, 0, 0, 0)
ConsoleWrite(">>> Returned hresult = " & Hex($iRet, 8) & @CRLF)
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<




MsgBox(0, "Info", "Press OK to exit")

$oRegistrationInfo.quit

 

Posted

Yeah I discovered that Itypeinfo doesn't recurse through the inherited objects and was in the process of implementing what you just plastered :P

 

Posted

@trancexx Thanks for the struct* <Pointer> hehe that cleans up the code a bit

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <array.au3>
#include <WinAPI.au3>

Global Enum $DISPATCH_METHOD = 0x1, $DISPATCH_PROPERTYGET = 0x2, $DISPATCH_PROPERTYPUT = 0x4, $DISPATCH_PROPERTYPUTREF = 0x8

#cs
    Global Enum $VT_EMPTY = 0, $VT_NULL = 1, $VT_I2 = 2, $VT_I4 = 3, $VT_R4 = 4, _
    $VT_R8 = 5, $VT_CY = 6, $VT_DATE = 7, $VT_BSTR = 8, $VT_DISPATCH = 9, _
    $VT_ERROR = 10, $VT_BOOL = 11, $VT_VARIANT = 12, $VT_UNKNOWN = 13, _
    $VT_DECIMAL = 14, $VT_I1 = 16, $VT_UI1 = 17, $VT_UI2 = 18, $VT_UI4 = 19, _
    $VT_I8 = 20, $VT_UI8 = 21, $VT_INT = 22, $VT_UINT = 23, $VT_VOID = 24, _
    $VT_HRESULT = 25, $VT_PTR = 26, $VT_SAFEARRAY = 27, $VT_CARRAY = 28, _
    $VT_USERDEFINED = 29, $VT_LPSTR = 30, $VT_LPWSTR = 31, $VT_FILETIME = 64, _
    $VT_BLOB = 65, $VT_STREAM = 66, $VT_STORAGE = 67, $VT_STREAMED_OBJECT = 68, _
    $VT_STORED_OBJECT = 69, $VT_BLOB_OBJECT = 70, $VT_CF = 71, $VT_CLSID = 72, _
    $VT_VECTOR = 0x1000, $VT_ARRAY = 0x2000, $VT_BYREF = 0x4000, $VT_RESERVED = 0x8000, _
    $VT_ILLEGAL = 0xffff, $VT_ILLEGALMASKED = 0xfff, $VT_TYPEMASK = 0xfff
#ce

Func __CreateVariant($pVt = 0)
    Local Const $tagVARIANT_PTR = "word vt;word r1;word r2;word r3;ptr data; ptr"
    ; Thanks @AutoItObject-Team, LarsJ?
    ; The Variant structure takes up 16/24 bytes when running 32/64 bit
    ; Space for the data element at the end represents 2 pointers
    ; This is 8 bytes running 32 bit and 16 bytes running 64 bit
    Local $tVariant
    If $pVt == 0 Then
        $tVariant = DllStructCreate($tagVARIANT_PTR)
        $pVt = DllStructGetPtr($tVariant)
        DllCall("OleAut32.dll", "NONE", "VariantInit", "ptr", $pVt)
        If @error Then Return SetError(@error, 0, 0)
    Else
        $tVariant = DllStructCreate($tagVARIANT_PTR, $pVt)
    EndIf
    Return SetError(0, $pVt, $tVariant)
EndFunc   ;==>__CreateVariant

Func __SetVariant(ByRef $tVariant, $iVarType, $pData, $sDataType = "ptr")

    Local $iError
    If IsDllStruct($tVariant) Then
        If Not DllStructSetData(DllStructCreate("dword", DllStructGetPtr($tVariant, "vt")), 1, $iVarType) Then
            $iError = BitOR($iError, 1)
        EndIf
        If Not DllStructSetData(DllStructCreate($sDataType, DllStructGetPtr($tVariant, "data")), 1, $pData) Then
            $iError = BitOR($iError, 2)
        EndIf
    Else
        $iError = 3
    EndIf

    Return SetError($iError, Null, ($iError = 0))
EndFunc   ;==>__SetVariant

Func __SysFreeString($pBstr, $vDll = "OleAut32.dll")
    DllCall($vDll, "NONE", "SysFreeString", "ptr", $pBstr)
    Return SetError(@error, 0, (@error = 0))
EndFunc   ;==>__SysFreeString

Func DispatchParameters($MemberType, $Type1 = "", $Param1 = 0, $Type2 = "", $Param2 = 0 _
        , $Type3 = "", $Param3 = 0, $Type4 = "", $Param4 = 0, $Type5 = 0, $Param5 = 0, $Type6 = "", $Param6 = 0 _
        , $Type7 = "", $Param7 = 0, $Type8 = "", $Param8 = 0, $Type9 = "", $Param9 = 0, $Type10 = "", $Param10 = 0 _
        , $Type11 = "", $Param11 = 0, $Type12 = "", $Param12 = 0, $Type13 = "", $Param13 = 0, $Type14 = "", $Param14 = 0 _
        , $Type15 = "", $Param15 = 0, $Type16 = "", $Param16 = 0, $Type17 = "", $Param17 = 0, $Type18 = "", $Param18 = 0 _
        , $Type19 = "", $Param19 = 0, $Type20 = "", $Param20 = 0, $Type21 = "", $Param21 = 0, $Type22 = "", $Param22 = 0 _
        , $Type23 = "", $Param23 = 0, $Type24 = "", $Param24 = 0, $Type25 = "", $Param25 = 0, $Type26 = "", $Param26 = 0 _
        , $Type27 = "", $Param27 = 0, $Type28 = "", $Param28 = 0, $Type29 = "", $Param29 = 0, $Type30 = "", $Param30 = 0)
    #forceref $Type1, $Param1, $Type2, $Param2, $Type3, $Param3, $Type4, $Param4, $Type5, $Param5 ;
    #forceref $Type6, $Param6, $Type7, $Param7, $Type8, $Param8, $Type9, $Param9, $Type10, $Param10 ;
    #forceref $Type11, $Param11, $Type12, $Param12, $Type13, $Param13, $Type14, $Param14, $Type15, $Param15 ;
    #forceref $Type16, $Param16, $Type17, $Param17, $Type18, $Param18, $Type19, $Param19, $Type20, $Param20 ;
    #forceref $Type21, $Param21, $Type22, $Param22, $Type23, $Param23, $Type24, $Param24, $Type25, $Param25 ;
    #forceref $Type26, $Param26, $Type27, $Param27, $Type28, $Param28, $Type29, $Param29, $Type30, $Param30 ;
    If ((@NumParams > 1) And (Mod((@NumParams - 1), 2) <> 0)) Then Return SetError(1, 0, 0)
    Local $NumParams = ((@NumParams - 1) / 2)
    ;WOLF9228 https://www.autoitscript.com/forum/topic/157680-exe-server-idispatch-call/?tab=comments#comment-1142824
    ;Local Const $tagVariant = "word vt;word r1;word r2;word r3;ptr data; ptr"
    Local Static $VariantSize = DllStructGetSize(__CreateVariant())
    ;
    Local Const $tagDISPPARAMS = "ptr rgvarg;ptr rgdispidNamedArgs;UINT cArgs;UINT cNamedArgs;" & _
            "STRUCT;INT64 rgdispidNamedArgsSt;byte rgvararg[" & ($VariantSize * $NumParams) & "];ENDSTRUCT" ;Not part of DISPPARAMS
    Local $tDispParams = DllStructCreate($tagDISPPARAMS)
    Local $rgvargPtr = DllStructGetPtr($tDispParams, "rgvararg")

    Local $nReturn[$NumParams + 1][2], $Variant = 0, $Value, $DataType, $DataTypeNu, $DISPID_PROPERTYPUT = -3

    For $i = 1 To $NumParams

        $Variant = __CreateVariant($rgvargPtr + (($NumParams - $i) * $VariantSize))

        $Value = Eval("Param" & $i)
        $DataType = Eval("Type" & $i)

        $DataTypeNu = GetDataTypeNu($DataType)
        If @error Then Return SetError(3, 0, 0)

        Local $BOOLREF = @extended
        $DataType = StringReplace($DataType, "*", "")
        $nReturn[$i][0] = $BOOLREF

        Switch $DataType
            Case "BSTR", "DISPATCH", "UNKNOWN", "ARRAY"
                If $BOOLREF Then
                    $nReturn[$i][1] = MakByRef()
                    __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                Else
                    $nReturn[$i][1] = $Value
                    __SetVariant($Variant, $DataTypeNu, $Value)
                EndIf

            Case "VARIANT", "DECIMAL"
                If Not IsPtr($Value) Or $Value = Ptr(0) Then Return SetError(4, 0, 0)
                $nReturn[$i][1] = $Value
                __SetVariant($Variant, $DataTypeNu, $Value)

            Case "CY"
                If $BOOLREF Then
                    $nReturn[$i][1] = MakByRef("INT64")
                    __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                Else
                    $nReturn[$i][1] = $Value
                    __SetVariant($Variant, $DataTypeNu, $Value, "INT64")
                EndIf

            Case "DATE"
                If $BOOLREF Then
                    $nReturn[$i][1] = MakByRef("double")
                    __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                Else
                    $nReturn[$i][1] = $Value
                    __SetVariant($Variant, $DataTypeNu, $Value, "double")
                EndIf

            Case "ERROR" ;Scode
                If $BOOLREF Then
                    $nReturn[$i][1] = MakByRef("long")
                    __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                Else
                    $nReturn[$i][1] = $Value
                    __SetVariant($Variant, $DataTypeNu, $Value, "long")
                EndIf

            Case "VBOOL" ;VARIANT_BOOL short A 16-bit ;typedef short VARIANT_BOOL;
                If $BOOLREF Then
                    $nReturn[$i][1] = MakByRef("short")
                    __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                Else
                    $nReturn[$i][1] = $Value
                    __SetVariant($Variant, $DataTypeNu, $Value, "short")
                EndIf

            Case Else
                If $BOOLREF Then
                    $nReturn[$i][1] = MakByRef($DataType)
                    __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                Else
                    $nReturn[$i][1] = $Value
                    __SetVariant($Variant, $DataTypeNu, $Value)
                EndIf

        EndSwitch
    Next
    DllStructSetData($tDispParams, "cArgs", $NumParams)
    ConsoleWrite("Params = " & $NumParams & " pVarArg (" & $rgvargPtr & ")" & @CRLF)
    DllStructSetData($tDispParams, "rgvarg", $rgvargPtr)
    ; When you use IDispatch::Invoke() with DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF,
    ; you have to specially initialize the cNamedArgs and rgdispidNamedArgs elements of your DISPPARAMS structure
    If BitAND($MemberType, $DISPATCH_PROPERTYPUT) Or BitAND($MemberType, $DISPATCH_PROPERTYPUTREF) Then
        DllStructSetData($tDispParams, "rgdispidNamedArgsSt", $DISPID_PROPERTYPUT)
        DllStructSetData($tDispParams, "rgdispidNamedArgs", DllStructGetPtr($tDispParams, "rgdispidNamedArgsSt"))
        DllStructSetData($tDispParams, "cNamedArgs", 1)
    EndIf

    Return $tDispParams
EndFunc   ;==>DispatchParameters

Func Get_IDispatch_Interface(ByRef $oObject)
    ;Retrieves $oITypeInfo
    ; === IDispatch interface ===

    ; http://msdn.microsoft.com/en-us/library/windows/desktop/ms221608(v=vs.85).aspx

    ; Exposes objects, methods and properties to programming tools and other applications that support Automation.
    ; COM components implement the IDispatch interface to enable access by Automation clients, such as Visual Basic.

    Local Const $sIID_IDispatch = "{00020400-0000-0000-C000-000000000046}"

    Local Const $dtag_IDispatch = _
            "GetTypeInfoCount hresult(dword*);" & _ ; Retrieves the number of type information interfaces that an object provides (either 0 or 1).
            "GetTypeInfo hresult(dword;dword;ptr*);" & _ ; Gets the type information for an object.
            "GetIDsOfNames hresult(struct*;struct*;dword;dword;struct*);" & _ ; Maps a single member and an optional set of argument names to a corresponding set of integer DISPIDs, which can be used on subsequent calls to Invoke.
            "Invoke hresult(dword;struct*;dword;word;struct*;struct*;ptr;uint*);" ; Provides access to properties and methods exposed by an object.
    Local $iError, $iResult = 0

    While True
        If (Not IsObj($oObject)) Then
            ConsoleWrite("Init Error: Invalid Object")
            $iError = 1
            ExitLoop
        EndIf

        If ObjName($oObject, 1) <> "InterfaceDispatch" Then
            $oObject = ObjCreateInterface($oObject, $sIID_IDispatch, $dtag_IDispatch) ;Try To get IDispatch
        EndIf

        If ObjName($oObject, 1) <> "InterfaceDispatch" Then
            ConsoleWrite("Init Error: Not InterfaceDispatch based")
            $iError = 2
            ExitLoop
        EndIf
        ExitLoop
    WEnd
    Return SetError($iError, $iResult, $oObject)
EndFunc   ;==>Get_IDispatch_Interface

Func GetDataTypeNu($DataType, $SafeArray_vt = False) ;$SafeArray_vt See SafeArrayCreate Func
    ;WOLF9228 https://www.autoitscript.com/forum/topic/157680-exe-server-idispatch-call/?tab=comments#comment-1142824
    ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms683835%28v=vs.85%29.aspx
    ;There are two main types of servers, in-process and out-of-process. In-process servers are implemented in a
    ;dynamic linked library (DLL), and out-of-process servers are implemented in an executable file (EXE). Out-of-process
    ;servers can reside either on the local computer or on a remote computer. In addition, COM provides a mechanism that
    ;allows an in-process server (a DLL) to run in a surrogate EXE process to gain the advantage of being able to run the
    ;process on a remote computer. For more information, see DLL Surrogates.

    ;(in Exe Server) Ptr Of Proc Or Ptr Of Out Struct Not Allowed Only
    ;Ptr Of Data Type like (int* DllStructGetPtr(DllStructCreate("int")) ,SHORT* DllStructGetPtr(DllStructCreate("SHORT")) ) or
    ;((ARRAY A SAFEARRAY pointer),(DISPATCH pointer),(UNKNOWN pointer),(BSTR pointer)) Or
    ;Ptr Of VARIANT Struct Or Ptr Of DECIMAL Struct ; See code in thes Func

    ;VARIANT structure
    ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms221627%28v=vs.85%29.aspx

    ;VARENUM enumeration
    ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms221170%28v=vs.85%29.aspx
    ;Variant conversion
    Local Enum $AIVT_INT_PTR = -2, $AIVT_LONG_PTR = -2, $AIVT_LRESULT = -2, $AIVT_LPARAM = -2, _
            $AIVT_PTR = -1, $AIVT_UINT_PTR = -1, $AIVT_ULONG_PTR = -1, $AIVT_DWORD_PTR = -1, $AIVT_WPARAM = -1, _
            $AIVT_SHORT = 2, $AIVT_INT32 = 3, $AIVT_HANDLE = 3, $AIVT_HWND = 3, $AIVT_FLOAT = 4, $AIVT_DOUBLE = 5, $AIVT_CY = 6, _
            $AIVT_DATE = 7, $AIVT_BSTR = 8, $AIVT_DISPATCH = 9, $AIVT_ERROR = 10, $AIVT_VBOOL = 11, _
            $AIVT_VARIANT = 12, $AIVT_UNKNOWN = 13, $AIVT_DECIMAL = 14, $AIVT_BYTE = 17, $AIVT_BOOLEAN = 17, _
            $AIVT_USHORT = 18, $AIVT_WORD = 18, $AIVT_UINT32 = 19, $AIVT_INT64 = 20, $AIVT_UINT64 = 21, _
            $AIVT_INT = 22, $AIVT_LONG = 22, $AIVT_BOOL = 22, _
            $AIVT_UINT = 23, $AIVT_ULONG = 23, $AIVT_DWORD = 23, _
            $AIVT_NONE = 24, $AIVT_ARRAY = 0X2000, $AIVT__BYREF = 0x4000 ;
    #forceref $AIVT_INT_PTR, $AIVT_LONG_PTR, $AIVT_LRESULT, $AIVT_LPARAM, $AIVT_PTR, $AIVT_UINT_PTR, $AIVT_ULONG_PTR
    #forceref $AIVT_DWORD_PTR, $AIVT_WPARAM, $AIVT_SHORT, $AIVT_INT32, $AIVT_HANDLE, $AIVT_HWND, $AIVT_FLOAT, $AIVT_DOUBLE, $AIVT_CY ;
    #forceref $AIVT_DATE, $AIVT_BSTR, $AIVT_DISPATCH, $AIVT_ERROR, $AIVT_VBOOL ;
    #forceref $AIVT_VARIANT, $AIVT_UNKNOWN, $AIVT_DECIMAL, $AIVT_BYTE, $AIVT_BOOLEAN ;
    #forceref $AIVT_USHORT, $AIVT_WORD, $AIVT_UINT32, $AIVT_INT64, $AIVT_UINT64, $AIVT_INT, $AIVT_LONG, $AIVT_BOOL ;
    #forceref $AIVT_UINT, $AIVT_ULONG, $AIVT_DWORD, $AIVT_NONE, $AIVT_ARRAY, $AIVT__BYREF ;

    Local $DataTypeNu = 0
    Local $BOOLREF = (StringInStr($DataType, "*") <> 0)
    $DataType = StringReplace(StringUpper($DataType), "*", "")

    $DataTypeNu = Eval("AIVT_" & $DataType)

    If $DataTypeNu == $AIVT_INT_PTR Then ;INT PTR
        If (@AutoItX64) Then
            $DataTypeNu = 20 ;VT_I8
        Else
            $DataTypeNu = 3 ;VT_I4
        EndIf
    ElseIf $DataTypeNu == $AIVT_PTR Then
        If (@AutoItX64) Then
            $DataTypeNu = 21 ;VT_UI8
        Else
            $DataTypeNu = 19 ;VT_UI4
        EndIf
    ElseIf $DataTypeNu == $AIVT_VARIANT Then ;pointer Of VARIANT structure ;[in,out] ([in,out] VARIANT pointer Only)
        If Not ($SafeArray_vt) Then $BOOLREF = True ; (Data Type Of VARIANT structure With ByRef Only)
        ;( Data Type Of $SafeArray_vt WithOut ByRef Only) See SafeArrayCreate Func
    ElseIf $DataTypeNu == $AIVT_DECIMAL Then ;DECIMAL See GetDecimalSt Func / pointer Of DECIMAL structure ;[in,out] ([in,out] DECIMAL pointer Only)
        If Not ($SafeArray_vt) Then $BOOLREF = True ; (Data Type Of DECIMAL structure With ByRef Only)
        ;( Data Type Of $SafeArray_vt WithOut ByRef Only) See SafeArrayCreate Func
    EndIf
    ConsoleWrite("Datatype " & $DataType & ":" & $DataTypeNu & @CRLF)
    If $DataTypeNu <= 0 Then Return SetError(1, 0, -1)

    If ($BOOLREF) Then $DataTypeNu = BitOR($AIVT__BYREF, $DataTypeNu)
    Return SetError(0, $BOOLREF, $DataTypeNu)
EndFunc   ;==>GetDataTypeNu

Func MakByRef($DataType = "ptr")
    Return DllStructCreate(String($DataType & " ByRef"))
EndFunc   ;==>MakByRef

Func __SysAllocStringLen($sString, $vDll = "OleAut32.dll")
    ;NOTE!, YOU Need To Free pBstr When you are finished with it ;__SysFreeString($pBstr)
    Local $iError

    Local $aRet = DllCall($vDll, "ptr", "SysAllocStringLen", "wstr", $sString, "uint", StringLen($sString))

    If Not @error And IsArray($aRet) Then
        If $aRet[0] <> Null Then
            Return $aRet[0]
        Else
            $iError = 55
        EndIf
    Else
        $iError = @error
    EndIf

    Return SetError($iError, Null, Null)
EndFunc   ;==>__SysAllocStringLen

Func SetPropertyByName(ByRef $oObject, $sMember, $tParams)
    Local $iMemId = 0
    Local $iArgErr = 0
    Local $hRes = 0x1
    Local Const $tIID_NULL = DllStructCreate($tagGUID)
    Local Const $LOCALE_SYSTEM_DEFAULT = 2048

    $iMemId = __GetMemberId($oObject, $sMember)

    If $iMemId > 0 Then
        ConsoleWrite("Found Member Id: " & $iMemId & @CRLF)

        Local $tDispatchPtr = DllStructCreate("ptr")
        DllStructSetData($tDispatchPtr, 1, $oObject)

        $hRes = $oObject.Invoke($iMemId, DllStructGetPtr($tIID_NULL), $LOCALE_SYSTEM_DEFAULT, $DISPATCH_PROPERTYPUT, $tParams, Null, Null, $iArgErr)
    EndIf
    Return SetError($hRes, $iArgErr, ($hRes == 0))
EndFunc   ;==>SetPropertyByName

Func __GetMemberId(ByRef $oObject, $sMember)
    Local $iMemId = 0
    Local $hRes = 0x1
    Local $iErr = 0
    Local Const $tIID_NULL = DllStructCreate($tagGUID)
    Local Const $LOCALE_SYSTEM_DEFAULT = 2048
    Local $tpMember = DllStructCreate("ptr pointer")

    Local $tMember = DllStructCreate("wchar[" & StringLen($sMember) + 1 & "]")
    DllStructSetData($tMember, 1, $sMember)
    DllStructSetData($tpMember, 1, DllStructGetPtr($tMember))
    Local $tDispId = DllStructCreate("long[1]")

    $hRes = $oObject.GetIDsOfNames(DllStructGetPtr($tIID_NULL), DllStructGetPtr($tpMember), 1, $LOCALE_SYSTEM_DEFAULT, DllStructGetPtr($tDispId)) ;
    $iErr = @error
    If Not $iErr And Not $hRes Then $iMemId = DllStructGetData($tDispId, 1)
    Return SetError($hRes, $iErr, $iMemId)
EndFunc   ;==>__GetMemberId
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#include <ie.au3>
Global $oRegistrationInfo = _IECreate()

Global $aRegistrationInfo[] = ['Left=100', 'Width=450']
Global $oObject = $oRegistrationInfo
Get_IDispatch_Interface($oObject)
__TS_TaskPropertiesSet($oObject, $aRegistrationInfo)

MsgBox(0, "Info", "Press OK to exit")

$oRegistrationInfo.quit

Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp
    If IsArray($aProperties) Then

        For $i = 0 To UBound($aProperties) - 1
            $aTemp = StringSplit($aProperties[$i], "=", 2) ; 2 -> $STR_NOCOUNT)
            If @error Then ContinueLoop

            ConsoleWrite("Command: $oObject." & $aTemp[0] & " = " & $aTemp[1] & @CRLF)
            Local $tDispParams = DispatchParameters($DISPATCH_PROPERTYPUT, "INT32", $aTemp[1])
            SetPropertyByName($oObject, $aTemp[0], $tDispParams)
            ConsoleWrite("Result : " & @error & @CRLF)
            ; If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

This is really the bare minimum IMO you need method calling too and possibly property_get although execute works for getting the info for the most part

 

The reason I liked using ITypeInfo: interface was becuase it offered the possibility to get the proper types automatically which was the eventual goal 

but seeing as it doesn't grab all the inherited interfaces as well I imagine you have to go through each one and run getDocumentation on them which is probably a giant PITA

 

Though I wonder if we could call $DISPATCH_PROPERTYGET on the member and intuit the types from that and fallback to the user defining it if that fails....

 

 

Posted (edited)
  On 9/7/2019 at 5:25 PM, trancexx said:

This shouldn't be a problem if you do it by calling Invoke on object yourself. The problem could be converting arguments from AutoIt type to COM type, which AutoIt does internally for you when working with objects the usual way. Anyway for something as simple as this you could do it like this

Expand  

Tadaaa!! the great Trancexx to the rescue. BRAVO! and thanks a lot @trancexx
... by putting your magic potion in a function makes it very easy to use. Thanks! :)

#include <ie.au3>

$oMyObject = _IECreate() ; just to have a "guinea pig" to experiment with
Global $aDesiredProperties[] = ['Left=10', 'Top=10', 'Width=270', 'Height=350', 'AddressBar=0', 'MenuBar=0', 'StatusBar=0', 'ToolBar=0']

For $i = 0 To UBound($aDesiredProperties) - 1
    $aSet = StringSplit($aDesiredProperties[$i], "=", 2) ; 2 -> $STR_NOCOUNT)
    _SetProperty($oMyObject, $aSet[0], $aSet[1])
Next

Local $sHTML = '0x3C21444F43545950452068746D6C3E3C68746D6C3E3C686561643E3C2F68656' & _
        '1643E3C626F64793E3C696D67207372633D2268747470733A2F2F61766174617273332E6' & _
        '7697468756275736572636F6E74656E742E636F6D2F752F31313537313636343F733D343' & _
        '63026753D616635313531636365333964633537303866633662383632346564666536653' & _
        '1373132393866643326763D34222077696474683D2231313122206865696768743D22313' & _
        '131223E3C62723E3C62723E54686973206769726C206973204D616769632120262378323' & _
        '736343C2F626F64793E3C2F68746D6C3E'

$oMyObject.document.Write(BinaryToString($sHTML))
$oMyObject.document.close()
$oMyObject.document.execCommand("Refresh")

MsgBox(0, "Info", "Press OK to exit")

$oMyObject.quit

Func _SetProperty($oObj, $sProperty, $vData)
    ; by Trancexx
    ; https://www.autoitscript.com/forum/topic/200129-set-object-properties-with-propertyname-and-value-taken-from-an-array/?do=findComment&comment=1436379

    ; Some constants used in code
    Const $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
            "AddRef dword();" & _
            "Release dword();"

    Const $tagIDispatch = $tagIUnknown & _
            "GetTypeInfoCount hresult(dword*);" & _
            "GetTypeInfo hresult(dword;dword;ptr*);" & _
            "GetIDsOfNames hresult(struct*;struct*;dword;dword;struct*);" & _
            "Invoke hresult(uint;struct*;dword;word;struct*;struct*;ptr;uint*);"

    Const $DISPID_PROPERTYPUT = -3
    Const $DISPATCH_PROPERTYPUT = 4
    Const $LOCALE_SYSTEM_DEFAULT = 0x800
    Const $tIID_NULL = DllStructCreate("byte[16]")
    Const $tagDISPPARAMS = "ptr rgvarg;ptr rgdispidNamedArgs;uint cArgs;uint cNamedArgs;"
    Const $VT_I4 = 3
    Const $tVARIANT = "word vt;word r1;word r2;word r3;ptr data; ptr"
    Const $sIID_IDispatch = "{00020400-0000-0000-C000-000000000046}"

    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; Superposed object on top of the original one
    Local $oObjMy = ObjCreateInterface($oObj, $sIID_IDispatch, $tagIDispatch, False)

    ; Collect ID number of the function/property/method, whatever
    $tDisp = DllStructCreate("dword")
    $tName = DllStructCreate("ptr")
    $tN = DllStructCreate("wchar[" & StringLen($sProperty) + 1 & "]")
    DllStructSetData($tN, 1, $sProperty)
    DllStructSetData($tName, 1, DllStructGetPtr($tN))

    $oObjMy.GetIDsOfNames($tIID_NULL, $tName, 1, $LOCALE_SYSTEM_DEFAULT, $tDisp)
    ; Tadaaa!
    $iDispId = DllStructGetData($tDisp, 1)


    ; Now build disp parameters
    $tDISPPARAMS = DllStructCreate($tagDISPPARAMS)
    $tDISPPARAMS.cNamedArgs = 1
    $tDispidNamed = DllStructCreate("uint")
    DllStructSetData($tDispidNamed, 1, $DISPID_PROPERTYPUT)
    $tDISPPARAMS.rgdispidNamedArgs = DllStructGetPtr($tDispidNamed)
    $tDISPPARAMS.cArgs = 1
    $tVar = DllStructCreate($tVARIANT)
    $tDISPPARAMS.rgvarg = DllStructGetPtr($tVar)

    ; Set desired value
    $tVar.vt = $VT_I4
    $tVar.data = $vData

    ; And call it
    $iRet = $oObjMy.Invoke($iDispId, $tIID_NULL, 0x800, $DISPATCH_PROPERTYPUT, $tDISPPARAMS, 0, 0, 0)

    Return (Hex($iRet, 8))
    ; ConsoleWrite(">>> Returned hresult = " & Hex($iRet, 8) & @CRLF)
    ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
EndFunc   ;==>_SetProperty

 

Edited by Chimp
changed html binary string

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Posted
  On 9/7/2019 at 7:52 PM, Bilgus said:

The reason I liked using ITypeInfo: interface was becuase it offered the possibility to get the proper types automatically which was the eventual goal 

but seeing as it doesn't grab all the inherited interfaces as well I imagine you have to go through each one and run getDocumentation on them which is probably a giant PITA

 

Though I wonder if we could call $DISPATCH_PROPERTYGET on the member and intuit the types from that and fallback to the user defining it if that fails....

 

 

Expand  

That's not unsolvable problem. In all cases you should call ITypeInfo.GetTypeAttr and check typekind of the TYPEATTR. If it'd be TKIND_INTERFACE then you should check wTypeFlags and see if it describes dual interface (bitwise check for TYPEFLAG_FDUAL), and if it does call GetRefTypeOfImplType on ITypeInfo with first argument being magic value of -1. Then all you need is to call GetRefTypeInfo with collected HREFTYPE to get correct ITypeInfo,

Posted (edited)

Seems to me that inserting ourselves between autoit and GetIDsFromNames would be a better way...

monoceres already showed how to patch the vtable here:

https://www.autoitscript.com/forum/topic/107678-hooking-into-the-idispatch-interface/

 

Instead lets just add a fake function Au3_CallByName and intercept that then put our desired function name in its place

and pass it on to the real GetIDsFromNames function

then we can still use autoit to do all the icky type conversions for us...

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

;Au3CallByName, Bilgus
Global $Au3_CallByName = 0
Local $hKernel32 = DllOpen("Kernel32.dll")
OnAutoItExitRegister(__CallByNameCleanup)

Func __CallByNameCleanup()
    Au3_CallByName_Init(Default, False) ;Unload
    DllClose($hKernel32)
EndFunc   ;==>__CallByNameCleanup

; Takes a pointer to the v-table in a class and replaces specified member Id in it to a new one.
Func __HookVTableEntry($pVtable, $iVtableOffset, $pHook, ByRef $pOldRet)
    ;;https://www.autoitscript.com/forum/topic/107678-hooking-into-the-idispatch-interface/
    Local Const $PAGE_READWRITE = 0x04
    Local $tpVtable = DllStructCreate("ptr", $pVtable)
    Local $szPtr = DllStructGetSize($tpVtable)
    Local $pFirstEntry, $pEntry, $tEntry, $aCall, $flOldProtect, $bStatus

    ; Dereference the vtable pointer
    $pFirstEntry = DllStructGetData($tpVtable, 1)
    $pEntry = $pFirstEntry + ($iVtableOffset * $szPtr)

    ; Make the memory free for all. Yay!
    $aCall = DllCall($hKernel32, "int", "VirtualProtect", "ptr", $pEntry, "long", $szPtr, "dword", $PAGE_READWRITE, "dword*", 0)

    If Not IsArray($aCall) Or Not $aCall[0] Then
        ConsoleWriteError("Error: Failed To hook vTable" & @CRLF)
        Return False
    EndIf
    $flOldProtect = $aCall[4]

    $tEntry = DllStructCreate("ptr", $pEntry)
    $pOldRet = DllStructGetData($tEntry, 1)
    If $pOldRet <> $pHook Then
        DllStructSetData($tEntry, 1, $pHook)
        $bStatus = True
    Else ;Already Hooked
        ConsoleWriteError("Error: vTable is already hooked" & @CRLF)
        $bStatus = False
    EndIf

    ;put the memory protect back how we found it
    DllCall($hKernel32, "int", "VirtualProtect", "ptr", $pEntry, "long", $szPtr, "dword", $flOldProtect, "dword*", 0)
    Return $bStatus
EndFunc   ;==>__HookVTableEntry

; Everytime autoit wants to call a method, get or set a property in a object it needs to go to
; IDispatch::GetIDsFromNames. This is our version of that function, note that by defining this ourselves
; we can fool autoit to believe that the object supports a lot of different properties/methods.
Func __IDispatch_GetIDsFromNames($pSelf, $riid, $rgszNames, $cNames, $lcid, $rgDispId)
    Local Const $DISP_E_UNKNOWNNAME = 0x80020006
    Local Static $pGIFN = __Pointer_GetIDsFromNames()

    If $Au3_CallByName Then
        Local $hRes, $aCall
        ;autoit only asks for one member

        $aCall = DllCall($hKernel32, "int", "lstrlenW", "struct*", _
                DllStructGetData(DllStructCreate("ptr", $rgszNames), 1))

        Local $iSz = IsArray($aCall) ? $aCall[0] + 1 : 256

        Local $tMember = DllStructCreate("wchar[" & $iSz & "]", _
                DllStructGetData(DllStructCreate("ptr", $rgszNames), 1))

        Local Static $tpMember = DllStructCreate("ptr pointer")
        ;ConsoleWrite("AutoIt wants to look up: " & DllStructGetData($tMember, 1) & @CRLF)

        If DllStructGetData($tMember, 1) == "Au3_CallByName" Then
            ;ConsoleWrite("CallByName: " & $Au3_CallByName & @CRLF)
            $tMember = DllStructCreate("wchar[" & StringLen($Au3_CallByName) + 1 & "]")
            DllStructSetData($tMember, 1, $Au3_CallByName)
            DllStructSetData($tpMember, 1, DllStructGetPtr($tMember))
            $rgszNames = $tpMember
            $Au3_CallByName = 0
        EndIf
    EndIf
    ;Call the original GetIDsFromNames
    $hRes = DllCallAddress("LRESULT", $pGIFN, "ptr", $pSelf, "ptr", $riid, _
            "struct*", $rgszNames, "dword", $cNames, "dword", $lcid, "ptr", $rgDispId)

    If @error Or Not IsArray($hRes) Then
        ConsoleWrite("Error: GetIDsFromNames: " & @error & @CRLF)
        Return $DISP_E_UNKNOWNNAME
    EndIf
    Return $hRes[0]
EndFunc   ;==>__IDispatch_GetIDsFromNames

Func __Pointer_GetIDsFromNames($ptr = 0)
    Local Static $pOldGIFN = $ptr
    If $ptr <> 0 Then $pOldGIFN = $ptr
    Return $pOldGIFN
EndFunc   ;==>__Pointer_GetIDsFromNames

Func Au3_CallByName_Init($classname = Default, $bHook = True)
    If $classname = Default Then $classname = "shell.application"
    Local $oObject, $pObject, $pHook, $pOldGIFN
    Local Const $iOffset_GetIDsFromNames = 5
    Local Static $IDispatch_GetIDsFromNames_Callback = 0

    If $bHook Then
        If $IDispatch_GetIDsFromNames_Callback = 0 Then
            $IDispatch_GetIDsFromNames_Callback = DllCallbackRegister("__IDispatch_GetIDsFromNames", "LRESULT", "ptr;ptr;ptr;dword;dword;ptr")
        EndIf
        $pHook = DllCallbackGetPtr($IDispatch_GetIDsFromNames_Callback)
    Else
        $pHook = __Pointer_GetIDsFromNames()
        If $pHook <= 0 Then Return ;Already Unloaded
    EndIf

    $oObject = ObjCreate($classname)
    $pObject = DllStructSetData(DllStructCreate("ptr"), 1, $oObject)

    If __HookVTableEntry($pObject, $iOffset_GetIDsFromNames, $pHook, $pOldGIFN) Then
        __Pointer_GetIDsFromNames($pOldGIFN) ;Save the original pointer to GetIDsFromNames
        If Not $bHook Then
            DllCallbackFree($IDispatch_GetIDsFromNames_Callback)
            $IDispatch_GetIDsFromNames_Callback = 0
        EndIf
    Else
        ;Error
    EndIf

    $oObject = 0
EndFunc   ;==>Au3_CallByName_Init

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;TESTS;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Au3_CallByName_Init()
#include <ie.au3>
Global $oRegistrationInfo = _IECreate()

Global $aRegistrationInfo[] = ['Left=1000', 'Width=450']
Global $oObject = $oRegistrationInfo

Local $oDictionary = ObjCreate("Scripting.Dictionary")

Local $oDictionary2 = ObjCreate("Scripting.Dictionary")


;Au3_CallByName_Init($oObject)
__TS_TaskPropertiesSet($oObject, $aRegistrationInfo)

MsgBox(0, "Info", "Press OK to exit")

$oRegistrationInfo.quit
$oRegistrationInfo = 0
$oObject = 0

Sleep(1000)
$Au3_CallByName = "Add"
$oDictionary.Au3_CallByName("test", "Dictionary Item: Test")
$Au3_CallByName = "Item"
ConsoleWrite($oDictionary.Au3_CallByName("test") & @CRLF)

Au3_CallByName_Init()

$Au3_CallByName = "Add"
$oDictionary2.Au3_CallByName("test2", "Dictionary Item: Test2")
$Au3_CallByName = "Item"
ConsoleWrite($oDictionary2.Au3_CallByName("test2") & @CRLF)

ConsoleWrite("NEW IE" & @CRLF & @CRLF)
$oRegistrationInfo = _IECreate()

__TS_TaskPropertiesSet($oRegistrationInfo, $aRegistrationInfo)

Au3_CallByName_Init(Default, False) ;Unload (Not Strictly Needed, Done on Script Close)

Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp
    If IsArray($aProperties) Then

        For $i = 0 To UBound($aProperties) - 1
            $aTemp = StringSplit($aProperties[$i], "=", 2) ; 2 -> $STR_NOCOUNT)
            If @error Then ContinueLoop

            ConsoleWrite("Command: $oObject." & $aTemp[0] & " = " & $aTemp[1] & @CRLF)
            $Au3_CallByName = $aTemp[0]
            $oObject.Au3_CallByName = $aTemp[1]
            ConsoleWrite("Result : " & @error & @CRLF)
            ; If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

 

That being said this code needs a lot still 

error checking namely but also a way to store the original pointer for GetIDsFromNames pointer for multiple objects either an array or maybe the dictionary object IDK yet

Edited by Bilgus
Cleaned up code a bit + made compatible with x64 + Added error handling + Unhooking

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