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

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:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

Posted

is this what you are looking for?

Spoiler
#include <StringConstants.au3>

Func __TS_TaskPropertiesSet(ByRef $oObject, $aProperties)
    Local $aTemp, $vResult, $o_obj
    #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)
            $o_obj = Eval("oObject")
            $o_obj($aTemp[0])=$aTemp[1];Execute("$oObject." & $aTemp[0] & "=" & $aTemp[1])
            $vResult = $oObject($aTemp[0])
ConsoleWrite($vResult & @CRLF)
            If @error Then Return SetError(1, @error, 0)
        Next
    EndIf
EndFunc   ;==>__TS_TaskPropertiesSet

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

 

 

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:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

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

Spoiler
#include <StringConstants.au3>

Global $__oTS_Error = ObjEvent("AutoIt.Error", "__TS_ErrorHandler")
Global $oService = ObjCreate("Schedule.Service")
Global $oTaskDefinition = $oService.NewTask(0)

Global $sTask = $oTaskDefinition.XmlText()
$sTask = StringReplace($sTask, "<RegistrationInfo />","<RegistrationInfo><Date>2005-10-11T13:21:17-08:00</Date><Author>AuthorName</Author><Version>1.0.0</Version></RegistrationInfo>")
$sTask = StringReplace($sTask, "<Actions />","<Actions><Exec><Command>notepad.exe</Command></Exec></Actions>")

$oTaskDefinition.XmlText = $sTask
Local $oRI = $oTaskDefinition.RegistrationInfo()
consoleWrite($oRI.Author & @crlf & $oTaskDefinition.XmlText() & @crlf)

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

Does that work for you?

 

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:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

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 11:15 AM, water said:

It seems Execute does a comparison and not an assignment.

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:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

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

Spoiler
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=n
#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>

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

Global Const $sIID_IDispatch = "{00020400-0000-0000-C000-000000000046}"
Global 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(ptr;ptr;dword;dword;ptr);" & _ ; 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;ptr;dword;word;ptr;ptr;ptr;ptr);" ; Provides access to properties and methods exposed by an object.
Global Enum $DISPATCH_METHOD = 0x1, $DISPATCH_PROPERTYGET = 0x2, $DISPATCH_PROPERTYPUT = 0x4, $DISPATCH_PROPERTYPUTREF = 0x8

; === ITypeInfo interface ===

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

;Used for reading information about objects.
;For example, an object browser tool can use ITypeInfo to extract information about the characteristics and capabilities of objects from type libraries

Global Const $sIID_ITypeInfo = "{00020401-0000-0000-C000-000000000046}"
Global Const $tRIID_ITypeInfo = _WinAPI_GUIDFromString($sIID_ITypeInfo)

Global Const $dtag_ITypeInfo = _
        "GetTypeAttr hresult(ptr*);" & _ ;Retrieves a TYPEATTR structure that contains the attributes of the type description
        "GetTypeComp hresult(ptr*);" & _ ;Retrieves the ITypeComp interface for the type description, which enables a client compiler to bind to the type description's members
        "GetFuncDesc hresult(uint;ptr*;);" & _ ;Retrieves the FUNCDESC structure that contains information about a specified function
        "GetVarDesc hresult(uint;ptr*);" & _ ;Retrieves a VARDESC structure that describes the specified variable
        "GetNames hresult(long;ptr;uint;uint*);" & _ ;Retrieves the variable with the specified member ID or the name of the property or method and the parameters that correspond to the specified function ID
        "GetRefTypeOfImplType hresult(dword;ptr);" & _ ;If a type description describes a COM class, it retrieves the type description of the implemented interface types
        "GetImplTypeFlags hresult(uint;int*);" & _ ;Retrieves the IMPLTYPEFLAGS enumeration for one implemented interface or base interface in a type description
        "GetIDsOfNames hresult(ptr;uint;long*);" & _ ;Maps between member names and member IDs, and parameter names and parameter IDs
        "Invoke hresult(ptr;int;word;ptr;ptr;ptr;uint*);" & _ ;Invokes a method, or accesses a property of an object, that implements the interface described by the type description
        "GetDocumentation hresult(long;ptr;ptr;dword*;ptr);" & _ ;Retrieves the documentation string, the complete Help file name and path, and the context ID for the Help topic for a specified type description
        "GetDllEntry hresult(long;int;ptr;ptr;word*);" & _ ;Retrieves a description or specification of an entry point for a function in a DLL
        "GetRefTypeInfo hresult(dword;ptr*);" & _ ;If a type description references other type descriptions, it retrieves the referenced type descriptions
        "AddressOfMember hresult(long;int;ptr);" & _ ;Retrieves the addresses of static functions or variables, such as those defined in a DLL
        "CreateInstance hresult(ptr;clsid;ptr);" & _ ;Creates a new instance of a type that describes a component object class (coclass)
        "GetMops hresult(long;ptr);" & _ ;Retrieves marshaling information
        "GetContainingTypeLib hresult(ptr;uint*);" & _ ;Retrieves the containing type library and the index of the type description within that type library
        "ReleaseTypeAttr none(ptr);" & _ ;Releases a TYPEATTR previously returned by ITypeInfo::GetTypeAttr
        "ReleaseFuncDesc none(ptr);" & _ ;Releases a FUNCDESC previously returned by ITypeInfo::GetFuncDesc
        "ReleaseVarDesc none(ptr);" ;Releases a VARDESC previously returned by ITypeInfo::GetVarDesc
#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

Global Const $tagIDLDESC = "STRUCT;dword dwRESERVEDIDL;ushort wIDLFlags;ENDSTRUCT;"

Global Const $tagTYPEDESC = "STRUCT;ptr lpdesc;ushort vt;ENDSTRUCT;"
Global Const $tagTYPEDESC_HREF = "dword hreftype;" ; &UNION^

Global Const $tagPARAMDESC = "STRUCT;ptr pparamdescex;ushort wParamFlags;ENDSTRUCT;"

Global Const $tagELEMDESC = "STRUCT;" & $tagTYPEDESC & $tagPARAMDESC & "ENDSTRUCT;" ;&UNIONS
Global Const $SIZEOF_tagElemDesc = DllStructGetSize(DllStructCreate($tagELEMDESC))

Global Const $tagTYPEATTR = _
        "STRUCT;" & _
        $tagGUID & _
        ";dword lcid;" & _
        "dword dwReserved;" & _
        "long memidConstructor;" & _
        "long memidDestructor;" & _
        "ptr lpstrSchema;" & _
        "ulong cbSizeInstance;" & _
        "int typekind;" & _
        "word cFuncs;" & _
        "word cVars;" & _
        "word cImplTypes;" & _
        "word cbSizeVft;" & _
        "word cbAlignment;" & _
        "word wTypeFlags;" & _
        "word wMajorVerNum;" & _
        "word wMinorVerNum;" & _
        $tagTYPEDESC & _
        $tagIDLDESC & _
        "ENDSTRUCT;"

Global Const $tagFUNCDESC = _
        "STRUCT;" & _
        "long memid;" & _
        "ptr lprgscode;" & _
        "ptr lprgelemdescParam;" & _
        "int funckind;" & _
        "int invkind;" & _
        "int callconv;" & _
        "short cParams;" & _
        "short cParamsOpt;" & _
        "short oVft;" & _
        "short cScodes;" & _
        $tagELEMDESC & _
        "word wFuncFlags;" & _
        "ENDSTRUCT;"

Func __CreateVariant($ptr = 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 $ptr == 0 Then
        $tVariant = DllStructCreate($tagVARIANT_PTR)
        Variant_Init($tVariant)
    Else
        $tVariant = DllStructCreate($tagVARIANT_PTR, $ptr)
    EndIf
    Return $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)
        If Not DllStructSetData(DllStructCreate($sDataType, DllStructGetPtr($tVariant, "data")), 1, $pData) Then $iError = BitOR($iError, 2)
    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)
        ;local $pVt = Variant_ChangeType($Variant, $DataTypeNu, 0)
        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"
                Switch $BOOLREF
                    Case True
                        $nReturn[$i][1] = MakByRef()
                        __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                    Case False
                        $nReturn[$i][1] = $Value
                        __SetVariant($Variant, $DataTypeNu, $Value)
                EndSwitch

            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"
                Switch $BOOLREF
                    Case True
                        $nReturn[$i][1] = MakByRef("INT64")
                        __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                    Case False
                        $nReturn[$i][1] = $Value
                        __SetVariant($Variant, $DataTypeNu, $Value, "INT64")
                EndSwitch

            Case "DATE"
                Switch $BOOLREF
                    Case True
                        $nReturn[$i][1] = MakByRef("double")
                        __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                    Case False
                        $nReturn[$i][1] = $Value
                        __SetVariant($Variant, $DataTypeNu, $Value, "double")
                EndSwitch

            Case "ERROR" ;Scode
                Switch $BOOLREF
                    Case True
                        $nReturn[$i][1] = MakByRef("long")
                        __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                    Case False
                        $nReturn[$i][1] = $Value
                        __SetVariant($Variant, $DataTypeNu, $Value, "long")
                EndSwitch

            Case "VBOOL" ;VARIANT_BOOL short A 16-bit ;typedef short VARIANT_BOOL;
                Switch $BOOLREF
                    Case True
                        $nReturn[$i][1] = MakByRef("short")
                        __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                    Case False
                        $nReturn[$i][1] = $Value
                        __SetVariant($Variant, $DataTypeNu, $Value, "short")
                EndSwitch

            Case Else

                Switch $BOOLREF
                    Case True
                        $nReturn[$i][1] = MakByRef($DataType)
                        __SetVariant($Variant, $DataTypeNu, DllStructGetPtr($nReturn[$i][1]))
                    Case False
                        $nReturn[$i][1] = $Value
                        __SetVariant($Variant, $DataTypeNu, $Value)
                EndSwitch

        EndSwitch
    Next
    DllStructSetData($tDispParams, "cArgs", $NumParams)
    ConsoleWrite("Params = " & $NumParams & " pVarArg (" & $rgvargPtr & ")" & @CRLF)
    DllStructSetData($tDispParams, "rgvarg", $rgvargPtr)
    If BitAND($MemberType, $DISPATCH_PROPERTYPUT) Then
        DllStructSetData($tDispParams, "rgdispidNamedArgsSt", $DISPID_PROPERTYPUT)
        DllStructSetData($tDispParams, "rgdispidNamedArgs", DllStructGetPtr($tDispParams, "rgdispidNamedArgsSt"))
        DllStructSetData($tDispParams, "cNamedArgs", 1)
    EndIf

    Return SetError(0, $nReturn, $tDispParams)
EndFunc   ;==>DispatchParameters

Func Extract_Interface_Functions(ByRef $oITypeInfo)
    ;Returns Functions and their parameters in AutoIt Array => return_type Function (int, uint*,Custom_TYPE...)
    Local $iResult, $iCtFuncs
    Local $aFunctions[0]

    Local $tTypeAttr = Get_ITypeInfo_Attr($oITypeInfo)

    If (Not @error) Then
        $iCtFuncs = DllStructGetData($tTypeAttr, "cFuncs") ;How many functions
        ReDim $aFunctions[$iCtFuncs][2] ;Init our Function array

        $oITypeInfo.ReleaseTypeAttr(DllStructGetPtr($tTypeAttr)) ;Important! Release TYPEATTR
    EndIf

    For $i = 0 To $iCtFuncs - 1
        Local $iMemId, $sFunctionName, $pFuncDesc

        $iResult = $oITypeInfo.GetFuncDesc($i, $pFuncDesc) ;Get Pointer to FUNCDESC
        If (Not $iResult) And $pFuncDesc Then
            Local $tFuncDesc = DllStructCreate($tagFUNCDESC, $pFuncDesc)
            $iMemId = DllStructGetData($tFuncDesc, "memid")
            $sFunctionName = Get_FunctionName($iMemId, $oITypeInfo) ;Member ID identifies function
            If (Not @error) Then
                $aFunctions[$i][0] = $iMemId
                $aFunctions[$i][1] = $sFunctionName
            EndIf

            $oITypeInfo.ReleaseFuncDesc($pFuncDesc) ;Important! Release FUNCDESC
        Else
            ConsoleWriteError("Error Retrieving Function Description: " & Hex($iResult) & @CRLF)
        EndIf
    Next
    Return $aFunctions
EndFunc   ;==>Extract_Interface_Functions

Func Get_FunctionName($iMemberId, $oITypeInfo)
    Local $iError, $sFunctionName, $iResult

    Local $aWstrName = WstrCreate_pp() ;**WSTR
    Local $ppWstr = WstrGetPointer_pp($aWstrName)
    If (Not @error) Then
        $iResult = $oITypeInfo.GetDocumentation($iMemberId, $ppWstr, Null, Null, Null) ;Get the Function Name
    EndIf

    If (Not @error) And (Not $iResult) And $aWstrName[0] Then
        $sFunctionName = WstrExtract_pp($aWstrName) ;Function Name **WSTR
        ;BSTR Freed Automatically
    Else
        $iError = 1
    EndIf
    Return SetError($iError, $iResult, $sFunctionName)
EndFunc   ;==>Get_FunctionName

Func Get_ITypeInfo_Attr(ByRef $oITypeInfo)
    ;Retrieves $tTypeAttr
    Local $tTypeAttr, $pTypeAttr, $iError

    If (Not $oITypeInfo.GetTypeAttr($pTypeAttr)) And $pTypeAttr Then
        $tTypeAttr = DllStructCreate($tagTYPEATTR, $pTypeAttr)
    Else
        $iError = 1
    EndIf

    Return SetError($iError, 0, $tTypeAttr)
EndFunc   ;==>Get_ITypeInfo_Attr

Func Get_ITypeInfo_Interface($oObject)
    ;Retrieves $oITypeInfo
    Local $iError, $iResult
    Local $pITypeInfo, $oITypeInfo

    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

        $iResult = $oObject.GetTypeInfo(0, 0, $pITypeInfo)
        If $pITypeInfo Then $oITypeInfo = ObjCreateInterface($pITypeInfo, $sIID_ITypeInfo, $dtag_ITypeInfo)

        If $iResult Or (Not IsObj($oITypeInfo)) Then
            ConsoleWrite("Init Error: Failed to retrieve interface")
            $iError = 3
            ExitLoop
        EndIf
        ExitLoop
    WEnd
    Return SetError($iError, $iResult, $oITypeInfo)
EndFunc   ;==>Get_ITypeInfo_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_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_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_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_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 Variant_Init($tVariant, $vDll = "OleAut32.dll")
    Local $pVt = DllStructGetPtr($tVariant)
    DllCall($vDll, "NONE", "VariantInit", "ptr", $pVt)
    If @error Then Return SetError(@error, 1, 0)
    Return $pVt ;BSTR Ptr
EndFunc   ;==>Variant_Init

Func WstrCreate_pp()
    ;Creates a Pointer to Pointer to **WSTR
    Local $aWstrObj[2]
    $aWstrObj[1] = DllStructCreate("ptr pointer")
    $aWstrObj[0] = DllStructGetPtr($aWstrObj[1])
    Return $aWstrObj
EndFunc   ;==>WstrCreate_pp

Func WstrExtract_pp($aWstrObj)
    ;Extracts Wstr from pointer to pointer to **WSTR
    Local $sString
    If UBound($aWstrObj) = 2 Then
        Local $pPointer = DllStructGetData($aWstrObj[1], "pointer")
        If IsPtr($pPointer) Then
            Local $tWstr = DllStructCreate("wchar data[65536]", $pPointer)
            $sString = DllStructGetData($tWstr, "data")
        EndIf
        __SysFreeString($pPointer) ; Free the BSTR
    EndIf
    Return $sString
EndFunc   ;==>WstrExtract_pp

Func WstrGetPointer_pp($aWstrObj)
    Local $pPointer = 0, $iError
    If UBound($aWstrObj) = 2 Then
        $pPointer = $aWstrObj[0]
    EndIf

    If (Not IsPtr($pPointer)) Then
        $iError = 1
        $pPointer = 0
    EndIf
    Return SetError($iError, 0, $pPointer)
EndFunc   ;==>WstrGetPointer_pp

Global $oService = ObjCreate("Schedule.Service")
Global $oTaskDefinition = $oService.NewTask(0)
Global $oRegistrationInfo = $oTaskDefinition.RegistrationInfo()
Global $oObject = $oRegistrationInfo

Local $oITypeInfo = Get_ITypeInfo_Interface($oObject)

Local $aFunctions = Extract_Interface_Functions($oITypeInfo)

Local $iMemId = 0, $sMember = "Author"
For $i = 0 To UBound($aFunctions, 1) - 1
    If $aFunctions[$i][1] == $sMember Then
        $iMemId = $aFunctions[$i][0]
        ExitLoop
    EndIf
Next

If $iMemId > 0 Then
    ConsoleWrite("Found Member Id: " & $iMemId & @CRLF)
    Local $iArgErr = 0, $hRes = 0
    Local $pBstr = __SysAllocStringLen("Bilgus")
    If @error Then ConsoleWrite("Error Creating BStr : " & @error & @CRLF)
    Local $DispParams = DispatchParameters($DISPATCH_PROPERTYPUT, "BSTR", $pBstr)
    If @error Then ConsoleWrite("Error Creating Param : " & @error & @CRLF)

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

    $hRes = $oITypeInfo.Invoke(DllStructGetData($tDispatchPtr, 1), $iMemId, $DISPATCH_PROPERTYPUT, DllStructGetPtr($DispParams), Null, Null, $iArgErr)
    __SysFreeString($pBstr)
EndIf
ConsoleWrite($sMember & ": " & Execute("$oObject." & $sMember) & @CRLF)
Exit

 

 

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:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

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

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

... "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 4: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 :)

 

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

 

♡♡♡

.

eMyvnE

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

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
8 hours ago, 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....

 

 

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,

♡♡♡

.

eMyvnE

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