water Posted August 31, 2019 Posted August 31, 2019 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
Bilgus Posted August 31, 2019 Posted August 31, 2019 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)
water Posted September 1, 2019 Author Posted September 1, 2019 (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: expandcollapse popup#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 September 1, 2019 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
Bilgus Posted September 1, 2019 Posted September 1, 2019 (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 September 1, 2019 by Bilgus
water Posted September 1, 2019 Author Posted September 1, 2019 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
Bilgus Posted September 1, 2019 Posted September 1, 2019 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..
Gianni Posted September 2, 2019 Posted September 2, 2019 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 Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
Bilgus Posted September 2, 2019 Posted September 2, 2019 best I could come up with is to patch the vtable to add a proper set method but that seems overkill or https://docs.microsoft.com/en-us/office/vba/Language/Reference/user-interface-help/callbyname-function using the VBA callbyname function which is probably the way to go
water Posted September 2, 2019 Author Posted September 2, 2019 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
Bilgus Posted September 2, 2019 Posted September 2, 2019 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
Bilgus Posted September 5, 2019 Posted September 5, 2019 (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 expandcollapse popup#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 September 5, 2019 by Bilgus Gianni and mLipok 1 1
Bilgus Posted September 5, 2019 Posted September 5, 2019 BTW Wolf9228 has some pretty awesome code for this here the function for making the DISPPARAMS structure is mostly his mLipok 1
water Posted September 5, 2019 Author Posted September 5, 2019 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
Gianni Posted September 7, 2019 Posted September 7, 2019 On 9/5/2019 at 7:18 AM, Bilgus said: .... However I did manage to make call by name work ... ... "mamma mia" ! ... sorry, how can I use this to make my little test script that I posted above work. Thanks Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
trancexx Posted September 7, 2019 Posted September 7, 2019 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 expandcollapse popup#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 Gianni and Bilgus 2 ♡♡♡ . eMyvnE
Bilgus Posted September 7, 2019 Posted September 7, 2019 Yeah I discovered that Itypeinfo doesn't recurse through the inherited objects and was in the process of implementing what you just plastered
Bilgus Posted September 7, 2019 Posted September 7, 2019 @trancexx Thanks for the struct* <Pointer> hehe that cleans up the code a bit expandcollapse popup#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....
Gianni Posted September 7, 2019 Posted September 7, 2019 (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! expandcollapse popup#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 May 1, 2020 by Chimp changed html binary string Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....
trancexx Posted September 8, 2019 Posted September 8, 2019 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
Bilgus Posted September 9, 2019 Posted September 9, 2019 (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... expandcollapse popup#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 September 10, 2019 by Bilgus Cleaned up code a bit + made compatible with x64 + Added error handling + Unhooking Gianni 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now