monoceres Posted January 3, 2010 Share Posted January 3, 2010 (edited) So, today my mind has been 100 % focused on something that on the surface looks really boring. The IDispatch interface. A lot of you probably heard the name IDispatch before. And you may even know that it's required by COM-objects used in autoit with the ObjCreate() function. So we're dealing with COM. Before reading any further I would recommend that you read the following wikipedia articles since that's what this article really is about. Link 1 Link 2.So what is IDispatch good for? It's a way for applications to resolve methods, properties and arguments during runtime instead when compiled. This is an extremely powerful feature that means that applications like AutoIt, VBScript and others that don't come shipped with thousands of lines of code from microsoft can utilize powerful code.We're going to take advantage of IDispatch's method of work. IDispatch exposes 4 methods for outside use. In our hooking application we will hook two of them. GetIdsFromNames and Invoke. These two are used to resolve member names in objects and finally use them. Why would we want to hook them? Because it provides some interesting opportunities, we can for example modify objects during runtime to bend over to our will.In the example below we're going to select one innocent victim. The only requirement for this victim is that it's an IDispatch interface. In my almighty wisdom I chose Shell.Application. So what will we do to poor Shell.Application? We will explore his vtable (you did read the wiki links?) and simply change the pointers that points to the real methods to custom autoit made callbacks.Now we have a problem. If we try to use ObjCreate function we will only get an abstract autoit object, this is no good since we wanna edit his inner workings, we need a pointer.So we simply create the object manually. Is this a free deal? No, because now we have a pointer, not the autoit object. Fortunately we exploit the "idispatch*" dllcall data type to overcome this problem.What we will do next is to examine the data coming form autoit and tell autoit that certain new names exists in the object - but in reality they really aren't. When autoit then tries to call this new method we're there too to execute some custom code. Enough chit chat. Here's the code. It's completely stand alone, no third party tools whatsoever, no assembly or nothing. Just Dll* functions.expandcollapse popupGlobal $hOle32=0 Global Const $tagGUID = "ulong;ushort;ushort;byte[8]" Global Const $tagCLSID = $tagGUID Global Const $tagUUID = $tagGUID Global Const $CLSCTX_INPROC_SERVER = 0x1 Global Const $S_OK = 0 Global Const $DISP_E_UNKNOWNNAME = 2147614726 Global Const $DISPID_UNKNOWN=4294967295 Global Const $DISP_E_MEMBERNOTFOUND=2147614723 ; Could be anything really. Global Const $HELLO_THERE_ID = 1337 ; Starts COM Func CoInitialize() $hOle32=DllOpen("Ole32.dll") DllCall($hOle32,"ulong","CoInitialize","ptr",0) EndFunc ; This will make a registry lookup to find out what the passed ; string really means. That is a GUID Func CLSIDFromProgID($ProgID) $clsid = DllStructCreate($tagCLSID) DllCall($hOle32,"long","CLSIDFromProgID","wstr",$ProgID,"ptr",DllStructGetPtr($clsid)) Return $clsid EndFunc ; Returns the UUID of IDispatch Func IDispatch_UUID() ; Autoit sets it all to null $uuid = DllStructCreate($tagUUID) DllStructSetData($uuid,1,132096) DllStructSetData($uuid,4,192,1) DllStructSetData($uuid,4,70,8) Return $uuid EndFunc ; Creates a instance of a COM object from a clsid and iid ; Usually done internally by autoit but we need the pointer. Func CoCreateInstance($clsid,$pUnkOuter,$ClsContext,$iid,$pOutObj) Return DllCall($hOle32,"long","CoCreateInstance","ptr",DllStructGetPtr($clsid),"ptr",$pUnkOuter,"dword",$ClsContext,"ptr",DllStructGetPtr($iid),"ptr",$pOutObj) EndFunc ; WRAPPER FOR IDISPATCH INTERFACES. ; This is like ObjCreate except this returns the raw pointer to the IDispatch interface. ; This is essential for hooking the methods of the IDispatch object. Func CreateIDispatchFromProgID($ProgID) ; Ptr to IDispatch object $pObj = DllStructCreate("ptr") $aCall = CoCreateInstance(CLSIDFromProgID($ProgID),0,$CLSCTX_INPROC_SERVER,IDispatch_UUID(),DllStructGetPtr($pObj)) Return DllStructGetData($pObj,1) EndFunc ; In the end we still want the autoit object. This function converts a raw pointer to an autoit object Func ConvertPtrToIDispatch($IDispatch_Ptr) ; This would have been 10000x easier if autoit had supported the idispatch* type in dllstructs... ; Fortunetely memcpy can copy the pointer into a idispatch*, lucky us. $ptr_struct=DllStructCreate("ptr") DllStructSetData($ptr_struct,1,$IDispatch_Ptr) $aCall = DllCall("ntdll.dll","ptr:cdecl","memcpy","idispatch*","","ptr",DllStructGetPtr($ptr_struct),"long",4) return $aCall[1] EndFunc ; Sets the MEM_EXECUTE_READWRITE flag on the specified memory. Use with care, I use because I'm lazy. Func UnprotectMemory($pointer,$size) DllCall("Kernel32.dll","int","VirtualProtect","ptr",$pointer,"long",$size,"dword",0x40,"dword*",0) EndFunc ; Returns the pointer the passed pointer points to ;) Func DereferencePointer($ptr) $tempstruct = DllStructCreate("ptr",$ptr) Return DllStructGetData($tempstruct,1) Endfunc ; Takes a pointer to the v-table in a class and replaces specified pointer in it to a new one. Func ReplaceVTableEntry($vtable_ptr,$offset,$new_ptr) ; Dereference the pointer $first_entry_ptr=DereferencePointer($vtable_ptr) $entry_pointer = $first_entry_ptr+$offset ; Make the memory free for all. Yay! UnprotectMemory($entry_pointer,4) $entry_struct = DllStructCreate("ptr",$entry_pointer) DllStructSetData($entry_struct,1,$new_ptr) EndFunc ; 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($self,$refiid,$str_array,$array_size,$context,$out_array) ; It's self explainable that autoit only asks for one member $str = DllStructCreate("wchar[256]",DereferencePointer($str_array)) ConsoleWrite("AutoIt wants to look up: "&DllStructGetData($str,1)&@CRLF) ; Autoit gave us an array with one element ready to accept the id of the member it requested. $ids = DllStructCreate("long",$out_array) ; If autoit tried to call method "HelloThere" then supply a valid ID and indicate success if DllStructGetData($str,1)=="HelloThere" Then DllStructSetData($ids,1,$HELLO_THERE_ID) return $S_OK ; ....else say it was an unknown name. ; We really should redirect execution to the original function here, but meh! Else DllStructSetData($ids,1,$DISPID_UNKNOWN) return $DISP_E_UNKNOWNNAME EndIf EndFunc ; Create the callback so we have a pointer to this function. $IDispatch_GetIDsFromNames_Callback = DllCallbackRegister("IDispatch_GetIDsFromNames","long","idispatch;ptr;ptr;int;int;ptr") $IDispatch_GetIDsFromNames_Callback_Ptr = DllCallbackGetPtr($IDispatch_GetIDsFromNames_Callback) ; This is called when a method is called, a property is set or get. ; This call also contains arguments returns and a lot other stuff. ; However in this trivial example we don't return anything and we don't take any arguments. Puh, could get messy Func IDispatch_Invoke($self,$dispID,$riid,$lcid,$wFlags,$pDispParams,$pVarResult,$pExceptInfo,$puArgErr) ;; Dump all parameters to console. ConsoleWrite("DispID: "&$dispID&@CRLF&"RIID: "&$riid&@CRLF&"LCID: "&$lcid&@CRLF&"wFlags: "&$wFlags&@CRLF& _ "pDispParams: "&$pDispParams&@CRLF&"pVarResult: "&$pVarResult&@CRLF&"pExceptInfo: "&$pExceptInfo&@CRLF&"puArgError: "&$puArgErr&@CRLF) ; Oh, autoit tries to use a macro we know! Lets do something If $dispID=$HELLO_THERE_ID Then MsgBox(0,"Hi","Hello There!") ; Give autoit the message that everything went according to plan return $S_OK Else ; Here we should Redirect execution to the original IDispatch::Invoke. ; But I'm lazy and we have avoid any assembly so for so lets make it stay that way Return $DISP_E_MEMBERNOTFOUND EndIf EndFunc ; Create callback $IDispatch_Invoke_Callback = DllCallbackRegister("IDispatch_Invoke","long","idispatch;long;dword;ushort;ptr;ptr;ptr;ptr;ptr") $IDispatch_Invoke_Callback_Ptr = DllCallbackGetPtr($IDispatch_Invoke_Callback) ; Initalize COM CoInitialize() ; Create a victim. Could be any COM object that inherits from IDispatch $obj_ptr = CreateIDispatchFromProgID("shell.application") ; Create autoit object as well. $obj = ConvertPtrToIDispatch($obj_ptr) ; Hook into the object ; Offset 20 & 24 is fifth entry in vtable. Look at IDispatch and IUnknown interfaces to see why ReplaceVTableEntry($obj_ptr,20,$IDispatch_GetIDsFromNames_Callback_Ptr) ReplaceVTableEntry($obj_ptr,24,$IDispatch_Invoke_Callback_Ptr) ; Perform magic. All this work has been so this little line would work. ; Computer science is truly amazing. $obj.HelloThere()What can we do with this newly found power? First off one can quite easily overcome the no ByRef in Mehtod calls limitation autoit have had forever. But most important: With this, AutoIt3 can, and will be an object oriented language, yes you read it right. Without even touching the core of autoit we can extend it into new heights. Inheritance, polymorphism and instances, just with some clever thinking.If anyone feel like they can contribute to the OO project and preferable knows at least basic COM feel free to contact me. Lastly a would like to thanks trancexx a bunch for ideas, code and clever thinking!Still reading? You're probably kinda alone Edited January 3, 2010 by monoceres Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
AdmiralAlkex Posted January 3, 2010 Share Posted January 3, 2010 This sounds awesome! 11 months ago I started on a UDF only to crash inte the ByRef limit, will be fun to see if I can get longer now .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface Link to comment Share on other sites More sharing options...
monoceres Posted January 3, 2010 Author Share Posted January 3, 2010 This sounds awesome!11 months ago I started on a UDF only to crash inte the ByRef limit, will be fun to see if I can get longer now Well it's possible now to build around it. Not saying I'm going to do it though I'm more into the other thing. Doing some incredible progress right now. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
AdmiralAlkex Posted January 3, 2010 Share Posted January 3, 2010 Well it's possible now to build around it. Not saying I'm going to do it though Can't you do both? How hard would it be anyway? Should I even bother? .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface Link to comment Share on other sites More sharing options...
monoceres Posted January 3, 2010 Author Share Posted January 3, 2010 Can't you do both? How hard would it be anyway? Should I even bother? I don't think you want to until my OO work is done. Stuff like this need to be implemented, and that's not too easy expandcollapse popupstruct tagVARIANT { union { struct __tagVARIANT { VARTYPE vt; WORD wReserved1; WORD wReserved2; WORD wReserved3; union { LONGLONG llVal; LONG lVal; BYTE bVal; SHORT iVal; FLOAT fltVal; DOUBLE dblVal; VARIANT_BOOL boolVal; _VARIANT_BOOL bool; SCODE scode; CY cyVal; DATE date; BSTR bstrVal; IUnknown *punkVal; IDispatch *pdispVal; SAFEARRAY *parray; BYTE *pbVal; SHORT *piVal; LONG *plVal; LONGLONG *pllVal; FLOAT *pfltVal; DOUBLE *pdblVal; VARIANT_BOOL *pboolVal; _VARIANT_BOOL *pbool; SCODE *pscode; CY *pcyVal; DATE *pdate; BSTR *pbstrVal; IUnknown **ppunkVal; IDispatch **ppdispVal; SAFEARRAY **pparray; VARIANT *pvarVal; PVOID byref; CHAR cVal; USHORT uiVal; ULONG ulVal; ULONGLONG ullVal; INT intVal; UINT uintVal; DECIMAL *pdecVal; CHAR *pcVal; USHORT *puiVal; ULONG *pulVal; ULONGLONG *pullVal; INT *pintVal; UINT *puintVal; struct __tagBRECORD { PVOID pvRecord; IRecordInfo *pRecInfo; } __VARIANT_NAME_4; } __VARIANT_NAME_3; } __VARIANT_NAME_2; DECIMAL decVal; } __VARIANT_NAME_1; } ; Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
AdmiralAlkex Posted January 3, 2010 Share Posted January 3, 2010 That thing is ugly It's even worse than SDL_RWops.... I think, I never understood that .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface Link to comment Share on other sites More sharing options...
trancexx Posted January 3, 2010 Share Posted January 3, 2010 monoceres made a great breakthrough with this. He opened a new door for AutoIt.And since I saw a bit of what followed the code from the first post I would say this - AutoIt will have classes built-in in the near future if there would be enough wisdom by the devs.Brilliant. ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
Datenshi Posted January 3, 2010 Share Posted January 3, 2010 I'm not surprised, monoceres and trancexx at it again. You sure know how to deliver quality code. RapidQueuer 2.4 - For Rapidshare.comOpensubtitles Hashing FuncRevision3 PlayerGTPlayer BetaIMDB & Poster Grabber v1.3Fetgrek.com - My Website Link to comment Share on other sites More sharing options...
James Posted January 3, 2010 Share Posted January 3, 2010 After monoceres explain why this could would be a useful asset to AutoIt, I sat there looking like this for a good few minutes.If what he told me he is doing at the moment, works... Then... Wow, AutoIt will be an even better language. Blog - Seriously epic web hosting - Twitter - GitHub - Cachet HQ Link to comment Share on other sites More sharing options...
monoceres Posted January 4, 2010 Author Share Posted January 4, 2010 I just got arguments to work. At the moment the following syntax is correct. $obj.Awesome("This is a MessageBox with automatic title") $obj.MessageBox("Regular","MessageBox") Func Awesome($self, $arg) Return $self.MessageBox("Awesome",$arg) EndFunc ;==>Awesome Func MessageBox($self,$arg,$arg2) MsgBox(0, $arg, $arg2) EndFunc ;==>MessageBox Properties should be next. They will be tricky. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
trancexx Posted January 4, 2010 Share Posted January 4, 2010 I just got arguments to work. At the moment the following syntax is correct. $obj.Awesome("This is a MessageBox with automatic title") $obj.MessageBox("Regular","MessageBox") Func Awesome($self, $arg) Return $self.MessageBox("Awesome",$arg) EndFunc ;==>Awesome Func MessageBox($self,$arg,$arg2) MsgBox(0, $arg, $arg2) EndFunc ;==>MessageBox Properties should be next. They will be tricky. Awesome! This is also a place where it would be necessary to introduce new syntax elements in AutoIt. ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
jvanegmond Posted January 4, 2010 Share Posted January 4, 2010 (edited) monoceres, could you give some practical appliances of this code other than introducing OO?I did a Lua implementation of OO once. It allows you to take no particular order of which classes you include (or load) first.Edit: You will probably not understand this if you don't know about metatables and metamethods: http://www.lua.org/pil/13.htmlLibrary (ignore annoying double white lines):expandcollapse popup// Manadar OO (to not conflict with any other OO systems) // it allows you to create classes with inheritance without knowing ahead of time which classes are loaded first // an important thing to know is that everything is built from tables. There is one table for each class (static) and one table for each class instance. // when calling a function/accessing a variable, this is the order of checking which table contains the function/variable: class instance -> class -> baseclass ( -> baseclass -> baseclass , etc. ) // Each class can access their base object with the self.base "keyword" // // some rules: // Be as strict as possible // Inheritance from Object class is enforced when no other class is defined. If you write your own Object class then no guarantees are given. // Properties and method that start with _ are private, and should be not be called outside of another object, unless the object calling should never be decoupled from the other // You should be much more careful about when you are dealing with a static class or when with an instance // // General OOP rules apply!! // Object class, the most simplest of classes, all other classes inherit from this Object = {} Object_instance_mt = {__index = Object} Object.Classname = "Object" function Object.Create() local instance = {} setmetatable(instance, Object_instance_mt) // This makes sure that if an instance of Object does not have method/var it checks for the statics (these are ToString() for example) return instance end function Object:ToString() return self.Classname end // Object class end Moo = {} local loadedClasses = {} // Contains a list of classes that are loaded/registered, these classes can be inherit from loadedClasses["Object"] = Object // Add the object class to the classes that are loaded local requireLoadClasses = {} // This will contain a list of classes that want to be loaded, but can't right now because their baseclass is not yet loaded/registered function Moo.getRegisteredClass(className) // Returns a loaded/registered class based on the classname. Search for "Object" this returns a static reference to Object. for k, v in pairs(loadedClasses) do if (k == className) then return v end end end function Moo.registerClass(class) // Registers a class as being loaded, removes this class from classes that needed loading, and checks again if any other classes need loading loadedClasses[class.Classname] = class // Remove this class from the list of classes that still need to be loaded for key, requireLoad in pairs(requireLoadClasses) do if (requireLoad.Class.Classname == class.Classname) then requireLoadClasses[key] = nil break end end // Maybe there are other classes waiting for this class to load Moo.checkClassesToLoad() end function Moo.checkClassesToLoad() // Re evaluates all the classes that are still waiting for their base object, and loads them if possible for loadMeKey, loadMe in pairs(requireLoadClasses) do // Loop through the classes we want to load for loadedClassName, loadedClass in pairs(loadedClasses) do // Loop through the classes that are loaded if (loadMe.baseClassName == loadedClassName) then // If we want to load a class which base class is already loaded Moo.inheritClassFrom(loadMe.Class, loadedClass) // Load this class end end end end function Moo.CreateClass(className, inheritFrom) if (!inheritFrom) then inheritFrom = "Object" end // you must always inherit from Object, so I am enforcing that print("Moo loading class: " .. tostring(className) .. " extends " .. tostring(inheritFrom)) local newClass = {} // Create a table for the new class (static member) newClass.Classname = className // Set the classname (static member) // Our class is now pretty much complete, but we still have to know about our base object before we initialize it baseClass = Moo.getRegisteredClass(inheritFrom) // Gets our base class, if it is loaded if (baseClass) then Moo.inheritClassFrom(newClass, baseClass) // Our base class is loaded, we inherit from it // The class is now fully loaded, with inheritance included else // The base object did not exist, let's wait for it and hope it arrives here shortly table.insert(requireLoadClasses, {Class = newClass, baseClassName = inheritFrom}) end return newClass end function Moo.inheritClassFrom(newClass, baseClass) print("Moo loaded class: " .. tostring(newClass.Classname) .. " extends " .. tostring(baseClass.Classname)) // A utility function to create the link between the static class and the static base class local newClass_instance_mt = { __index = newClass } local newClass_mt = { __index = baseClass } // This class inherits from the base class setmetatable(newClass, newClass_mt) function newClass.Create(param1, param2, param3, param4, param5) // This is the static function to create a new object (similar to the new keyword) local newClass_instance = baseClass.Create(param1, param2, param3, param4, param5) // Create our base class before we create ourselves (common practice) newClass_instance.base = baseClass // Set up the self.base keyword to be used in any class instances setmetatable( newClass_instance, newClass_instance_mt ) // Our class instances references our class object if it must return newClass_instance end // The class is now fully loaded, with inheritance included Moo.registerClass(newClass) endExample:expandcollapse popupinclude('moo.lua') -- Syntax for CreateClass is: -- variable to contain the class (static) = CreateClass("Class name") -- You can use another name for the variable that holds the class, but I don't recommend it (for clarity) Animal = Moo.CreateClass("Animal") Animal.Legs = 6 // Static variable function Animal:Walk() print("I am now walking.") end function Animal:Stop() print("I have now stopped walking.") end function Animal:ToString() return "An animal usually has " .. tostring(self.Legs) .. " legs" end // Dog inherits from Animal Dog = Moo.CreateClass("Dog", "Animal") Dog.Legs = 4 function Dog:ToString() // override ToString print("A dog has " .. self.Legs .. " legs") end function Dog:Stop() // override Stop print("Dogs don't stop!! :D") end // Let's try out the animal class anml = Animal.Create() // anml = new Animal() anml:Walk() // Tell the animal to walk anml:Stop() // Tell the animal to stop print(anml:ToString()) // ToString test //Output: // I am now walking. // I have now stopped walking. // An animal usually has 6 legs // Let's try out the dog class :D doggy = Dog.Create() doggy:Walk() doggy:Stop() print(doggy:ToString()) // Output: // I am now walking. // Dogs don't stop!! :D // A dog has 4 legs doggy.Legs = 2 print(doggy:ToString()) // Output: // A dog has 2 legs Edited January 4, 2010 by Manadar github.com/jvanegmond Link to comment Share on other sites More sharing options...
monoceres Posted January 4, 2010 Author Share Posted January 4, 2010 Awesome! This is also a place where it would be necessary to introduce new syntax elements in AutoIt. Yeah, trhe syntax for defining objects will not be very good. But it will work nevertheless monoceres, could you give some practical appliances of this code other than introducing OO? I did a Lua implementation of OO once. It allows you to take no particular order of which classes you include (or load) first. Edit: You will probably not understand this if you don't know about metatables and metamethods: http://www.lua.org/pil/13.html Library (ignore annoying double white lines): expandcollapse popup// Manadar OO (to not conflict with any other OO systems) // it allows you to create classes with inheritance without knowing ahead of time which classes are loaded first // an important thing to know is that everything is built from tables. There is one table for each class (static) and one table for each class instance. // when calling a function/accessing a variable, this is the order of checking which table contains the function/variable: class instance -> class -> baseclass ( -> baseclass -> baseclass , etc. ) // Each class can access their base object with the self.base "keyword" // // some rules: // Be as strict as possible // Inheritance from Object class is enforced when no other class is defined. If you write your own Object class then no guarantees are given. // Properties and method that start with _ are private, and should be not be called outside of another object, unless the object calling should never be decoupled from the other // You should be much more careful about when you are dealing with a static class or when with an instance // // General OOP rules apply!! // Object class, the most simplest of classes, all other classes inherit from this Object = {} Object_instance_mt = {__index = Object} Object.Classname = "Object" function Object.Create() local instance = {} setmetatable(instance, Object_instance_mt) // This makes sure that if an instance of Object does not have method/var it checks for the statics (these are ToString() for example) return instance end function Object:ToString() return self.Classname end // Object class end Moo = {} local loadedClasses = {} // Contains a list of classes that are loaded/registered, these classes can be inherit from loadedClasses["Object"] = Object // Add the object class to the classes that are loaded local requireLoadClasses = {} // This will contain a list of classes that want to be loaded, but can't right now because their baseclass is not yet loaded/registered function Moo.getRegisteredClass(className) // Returns a loaded/registered class based on the classname. Search for "Object" this returns a static reference to Object. for k, v in pairs(loadedClasses) do if (k == className) then return v end end end function Moo.registerClass(class) // Registers a class as being loaded, removes this class from classes that needed loading, and checks again if any other classes need loading loadedClasses[class.Classname] = class // Remove this class from the list of classes that still need to be loaded for key, requireLoad in pairs(requireLoadClasses) do if (requireLoad.Class.Classname == class.Classname) then requireLoadClasses[key] = nil break end end // Maybe there are other classes waiting for this class to load Moo.checkClassesToLoad() end function Moo.checkClassesToLoad() // Re evaluates all the classes that are still waiting for their base object, and loads them if possible for loadMeKey, loadMe in pairs(requireLoadClasses) do // Loop through the classes we want to load for loadedClassName, loadedClass in pairs(loadedClasses) do // Loop through the classes that are loaded if (loadMe.baseClassName == loadedClassName) then // If we want to load a class which base class is already loaded Moo.inheritClassFrom(loadMe.Class, loadedClass) // Load this class end end end end function Moo.CreateClass(className, inheritFrom) if (!inheritFrom) then inheritFrom = "Object" end // you must always inherit from Object, so I am enforcing that print("Moo loading class: " .. tostring(className) .. " extends " .. tostring(inheritFrom)) local newClass = {} // Create a table for the new class (static member) newClass.Classname = className // Set the classname (static member) // Our class is now pretty much complete, but we still have to know about our base object before we initialize it baseClass = Moo.getRegisteredClass(inheritFrom) // Gets our base class, if it is loaded if (baseClass) then Moo.inheritClassFrom(newClass, baseClass) // Our base class is loaded, we inherit from it // The class is now fully loaded, with inheritance included else // The base object did not exist, let's wait for it and hope it arrives here shortly table.insert(requireLoadClasses, {Class = newClass, baseClassName = inheritFrom}) end return newClass end function Moo.inheritClassFrom(newClass, baseClass) print("Moo loaded class: " .. tostring(newClass.Classname) .. " extends " .. tostring(baseClass.Classname)) // A utility function to create the link between the static class and the static base class local newClass_instance_mt = { __index = newClass } local newClass_mt = { __index = baseClass } // This class inherits from the base class setmetatable(newClass, newClass_mt) function newClass.Create(param1, param2, param3, param4, param5) // This is the static function to create a new object (similar to the new keyword) local newClass_instance = baseClass.Create(param1, param2, param3, param4, param5) // Create our base class before we create ourselves (common practice) newClass_instance.base = baseClass // Set up the self.base keyword to be used in any class instances setmetatable( newClass_instance, newClass_instance_mt ) // Our class instances references our class object if it must return newClass_instance end // The class is now fully loaded, with inheritance included Moo.registerClass(newClass) end Example: expandcollapse popupinclude('moo.lua') -- Syntax for CreateClass is: -- variable to contain the class (static) = CreateClass("Class name") -- You can use another name for the variable that holds the class, but I don't recommend it (for clarity) Animal = Moo.CreateClass("Animal") Animal.Legs = 6 // Static variable function Animal:Walk() print("I am now walking.") end function Animal:Stop() print("I have now stopped walking.") end function Animal:ToString() return "An animal usually has " .. tostring(self.Legs) .. " legs" end // Dog inherits from Animal Dog = Moo.CreateClass("Dog", "Animal") Dog.Legs = 4 function Dog:ToString() // override ToString print("A dog has " .. self.Legs .. " legs") end function Dog:Stop() // override Stop print("Dogs don't stop!! :D") end // Let's try out the animal class anml = Animal.Create() // anml = new Animal() anml:Walk() // Tell the animal to walk anml:Stop() // Tell the animal to stop print(anml:ToString()) // ToString test //Output: // I am now walking. // I have now stopped walking. // An animal usually has 6 legs // Let's try out the dog class :D doggy = Dog.Create() doggy:Walk() doggy:Stop() print(doggy:ToString()) // Output: // I am now walking. // Dogs don't stop!! :D // A dog has 4 legs doggy.Legs = 2 print(doggy:ToString()) // Output: // A dog has 2 legs Interesting. You wouldn't be interesting in helping my a little with this? I'm not sure that current lookup table is as flexible as I want it to. Another use for the code is as mentioned before, adding ByRef support fr parameters. I'm sure more uses can be found. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
jvanegmond Posted January 4, 2010 Share Posted January 4, 2010 I have full faith in your abilities as a programmer, you will do this just fine. Probably even better than me.. github.com/jvanegmond Link to comment Share on other sites More sharing options...
monoceres Posted January 4, 2010 Author Share Posted January 4, 2010 (edited) Read and wheep. Here's the first example of Object orientation in pure autoit without any preprocessor or anything.The code is unstable (I'm getting a random crash somewhere, annoying, freed memory is probably the error), not functional for x64 and the namespace is raped beyond recognition. But it works god damn it!For those not interested in the technical implementation just look at the last 20 lines. There's magic going on right there. expandcollapse popup#include <Array.au3> Global $hOle32 = 0 Global Const $VT_EMPTY = 0 Global Const $VT_NULL = 1 Global Const $VT_I2 = 2 Global Const $VT_I4 = 3 Global Const $VT_R4 = 4 Global Const $VT_R8 = 5 Global Const $VT_CY = 6 Global Const $VT_DATE = 7 Global Const $VT_BSTR = 8 Global Const $VT_DISPATCH = 9 Global Const $VT_ERROR = 10 Global Const $VT_BOOL = 11 Global Const $VT_VARIANT = 12 Global Const $VT_UNKNOWN = 13 Global Const $VT_DECIMAL = 14 Global Const $VT_I1 = 16 Global Const $VT_UI1 = 17 Global Const $VT_UI2 = 18 Global Const $VT_UI4 = 19 Global Const $VT_I8 = 20 Global Const $VT_UI8 = 21 Global Const $VT_INT = 22 Global Const $VT_UINT = 23 Global Const $VT_VOID = 24 Global Const $VT_HRESULT = 25 Global Const $VT_PTR = 26 Global Const $VT_SAFEARRAY = 27 Global Const $VT_CARRAY = 28 Global Const $VT_USERDEFINED = 29 Global Const $VT_LPSTR = 30 Global Const $VT_LPWSTR = 31 Global Const $VT_RECORD = 36 Global Const $VT_INT_PTR = 37 Global Const $VT_UINT_PTR = 38 Global Const $VT_FILETIME = 64 Global Const $VT_BLOB = 65 Global Const $VT_STREAM = 66 Global Const $VT_STORAGE = 67 Global Const $VT_STREAMED_OBJECT = 68 Global Const $VT_STORED_OBJECT = 69 Global Const $VT_BLOB_OBJECT = 70 Global Const $VT_CF = 71 Global Const $VT_CLSID = 72 Global Const $VT_VERSIONED_STREAM = 73 Global Const $VT_BSTR_BLOB = 0xfff Global Const $VT_VECTOR = 0x1000 Global Const $VT_ARRAY = 0x2000 Global Const $VT_BYREF = 0x4000 Global Const $VT_RESERVED = 0x8000 Global Const $VT_ILLEGAL = 0xffff Global Const $VT_ILLEGALMASKED = 0xfff Global Const $VT_TYPEMASK = 0xfff Global Const $tagGUID = "ulong;ushort;ushort;byte[8]" Global Const $tagCLSID = $tagGUID Global Const $tagUUID = $tagGUID Global Const $CLSCTX_INPROC_SERVER = 0x1 Global Const $S_OK = 0 Global Const $DISP_E_UNKNOWNNAME = 2147614726 Global Const $DISPID_UNKNOWN = 4294967295 Global Const $DISP_E_MEMBERNOTFOUND = 2147614723 Global Const $tagVARIANT = "ushort vt;ushort r1;ushort r2;ushort r3;uint64 data" Global Const $tagDISPPARAMS = "ptr rgvargs;ptr rgdispidNamedArgs;dword cArgs;dword cNamedArgs;" Global Const $DISPATCH_METHOD = 0x1 Global Const $DISPATCH_PROPERTYGET = 0x2 Global Const $DISPATCH_PROPERTYPUT = 0x4 Global Const $DISPATCH_PROPERTYPUTREF = 0x8 ; Starts COM Func CoInitialize() $hOle32 = DllOpen("Ole32.dll") DllCall($hOle32, "ulong", "CoInitialize", "ptr", 0) EndFunc ;==>CoInitialize ; This will make a registry lookup to find out what the passed ; string really means. That is a GUID Func CLSIDFromProgID($ProgID) $clsid = DllStructCreate($tagCLSID) DllCall($hOle32, "long", "CLSIDFromProgID", "wstr", $ProgID, "ptr", DllStructGetPtr($clsid)) Return $clsid EndFunc ;==>CLSIDFromProgID ; Returns the UUID of IDispatch Func IDispatch_UUID() ; Autoit sets it all to null $uuid = DllStructCreate($tagUUID) DllStructSetData($uuid, 1, 132096) DllStructSetData($uuid, 4, 192, 1) DllStructSetData($uuid, 4, 70, 8) Return $uuid EndFunc ;==>IDispatch_UUID ; Creates a instance of a COM object from a clsid and iid ; Usually done internally by autoit but we need the pointer. Func CoCreateInstance($clsid, $pUnkOuter, $ClsContext, $iid, $pOutObj) Return DllCall($hOle32, "long", "CoCreateInstance", "ptr", DllStructGetPtr($clsid), "ptr", $pUnkOuter, "dword", $ClsContext, "ptr", DllStructGetPtr($iid), "ptr", $pOutObj) EndFunc ;==>CoCreateInstance ; WRAPPER FOR IDISPATCH INTERFACES. ; This is like ObjCreate except this returns the raw pointer to the IDispatch interface. ; This is essential for hooking the methods of the IDispatch object. Func CreateIDispatchFromProgID($ProgID) ; Ptr to IDispatch object $pObj = DllStructCreate("ptr") $aCall = CoCreateInstance(CLSIDFromProgID($ProgID), 0, $CLSCTX_INPROC_SERVER, IDispatch_UUID(), DllStructGetPtr($pObj)) Return DllStructGetData($pObj, 1) EndFunc ;==>CreateIDispatchFromProgID ; In the end we still want the autoit object. This function converts a raw pointer to an autoit object Func ConvertPtrToIDispatch($pIDispatch) ; This would have been 10000x easier if autoit had supported the idispatch* type in dllstructs... ; Fortunetely memcpy can copy the pointer into a idispatch*, lucky us. Local $aCall = DllCall("kernel32.dll", "none", "RtlMoveMemory", _ "idispatch*", 0, _ "ptr*", $pIDispatch, _ "dword", 4) If @error Then Return SetError(1, 0, 0) EndIf Return $aCall[1] EndFunc ;==>ConvertPtrToIDispatch ; Sets the MEM_EXECUTE_READWRITE flag on the specified memory. Use with care, I use because I'm lazy. Func UnprotectMemory($pointer, $size) DllCall("Kernel32.dll", "int", "VirtualProtect", "ptr", $pointer, "long", $size, "dword", 0x40, "dword*", 0) EndFunc ;==>UnprotectMemory ; Returns the pointer the passed pointer points to ;) Func DereferencePointer($ptr) $tempstruct = DllStructCreate("ptr", $ptr) Return DllStructGetData($tempstruct, 1) EndFunc ;==>DereferencePointer ; Moves the vtable to a new position. useful if you want to add more entries. Func RelocateVTable($pObj, $pNew, $VTable_size) $vtable = DllStructCreate("ptr", $pObj) $vtable_ptr = DllStructGetData($vtable, 1) DllCall("kernel32.dll", "none", "RtlMoveMemory", "ptr", $pNew, "ptr", $vtable_ptr, "dword", $VTable_size) DllStructSetData($vtable, 1, $pNew) EndFunc ;==>RelocateVTable ; Allocate memory on the heap Func DynAlloc($dwSize) $hHeap = DllCall("Kernel32.dll", "ptr", "GetProcessHeap") $hHeap = $hHeap[0] $hMem = DllCall("Kernel32.dll", "ptr", "HeapAlloc", "ptr", $hHeap, "dword", 0x8, "dword", $dwSize) DllCall("Kernel32.dll", "none", "CloseHandle", "ptr", $hHeap) Return $hMem[0] EndFunc ;==>DynAlloc ; Takes a pointer to the v-table in a class and replaces specified pointer in it to a new one. Func ReplaceVTableEntry($vtable_ptr, $offset, $new_ptr) ; Dereference the pointer $first_entry_ptr = DereferencePointer($vtable_ptr) $entry_pointer = $first_entry_ptr + $offset ; Make the memory free for all. Yay! UnprotectMemory($entry_pointer, 4) $entry_struct = DllStructCreate("ptr", $entry_pointer) DllStructSetData($entry_struct, 1, $new_ptr) EndFunc ;==>ReplaceVTableEntry Func MakeQWORD($val1, $val2) $qword = DllStructCreate("uint64") $dwords = DllStructCreate("dword;dword", DllStructGetPtr($qword)) DllStructSetData($dwords, 1, $val1) DllStructSetData($dwords, 2, $val2) Return DllStructGetData($qword, 1) EndFunc ;==>MakeQWORD Func GetHIDWORD($qword_val) $qword = DllStructCreate("uint64") DllStructSetData($qword, 1, $qword_val) $dword = DllStructCreate("dword", DllStructGetPtr($qword) + 4) Return DllStructGetData($dword, 1) EndFunc ;==>GetHIDWORD Func GetLODWORD($qword_val) $qword = DllStructCreate("uint64") DllStructSetData($qword, 1, $qword_val) $dword = DllStructCreate("dword", DllStructGetPtr($qword)) Return DllStructGetData($dword, 1) EndFunc ;==>GetLODWORD Func CreateDynamicString($str) $dynmem = DynAlloc(StringLen($str) + 1) DllStructSetData(DllStructCreate("char[" & StringLen($str) + 1 & "]", $dynmem), 1, $str) Return $dynmem EndFunc ;==>CreateDynamicString Func ConstructLookupTable($pObj, $aNames) ; Set point in vtable $vtable_entry = DllStructCreate("ptr", DereferencePointer($pObj) + 4 * 7) ; Create dynamic memory for new lookup table $mem = DynAlloc(4 + (UBound($aNames) * 8)) ; Create lookup table, first element is number of element ; Must be sorted! $lookup_table = DllStructCreate("int;uint64[" & UBound($aNames) & "]", $mem) ; Set size of lookup table DllStructSetData($lookup_table, 1, UBound($aNames)) ; Modify vtable to point to the lookup table DllStructSetData($vtable_entry, 1, $mem) For $i = 0 To UBound($aNames) - 1 If StringLeft($aNames[$i][0], 1) = "@" Then ; This is a method DllStructSetData($lookup_table, 2, MakeQWORD(CreateDynamicString($aNames[$i][0]), CreateDynamicString($aNames[$i][1])), $i + 1) Else ; It's a property then. $variant_ptr = DynAlloc(16) ValueToCOMVariant($variant_ptr, $aNames[$i][1]) DllStructSetData($lookup_table, 2, MakeQWORD(CreateDynamicString($aNames[$i][0]), $variant_ptr), $i + 1) EndIf Next EndFunc ;==>ConstructLookupTable Func ChrPtrToString($ptr, $maxsize = 4096) $str_struct = DllStructCreate("char[" & $maxsize & "]", $ptr) Return DllStructGetData($str_struct, 1) EndFunc ;==>ChrPtrToString Func FindNameInLookupTable($pObj, $name) $vtable_entry = DllStructCreate("ptr", DereferencePointer($pObj) + 4 * 7) ; sizeof(ptr)*(num entries in idispatch) $header = DllStructCreate("int", DllStructGetData($vtable_entry, 1)) $lookup_table_size = DllStructGetData($header, 1) $lookup_table = DllStructCreate("int;uint64[" & $lookup_table_size & "]", DllStructGetData($vtable_entry, 1)) For $i = 1 To $lookup_table_size If $name = ChrPtrToString(GetLODWORD(DllStructGetData($lookup_table, 2, $i))) Then Return $i Next Return -1 EndFunc ;==>FindNameInLookupTable Func IDToValue($pObj, $id) $vtable_entry = DllStructCreate("ptr", DereferencePointer($pObj) + 4 * 7) $header = DllStructCreate("int", DllStructGetData($vtable_entry, 1)) $lookup_table_size = DllStructGetData($header, 1) $lookup_table = DllStructCreate("int;uint64[" & $lookup_table_size & "]", DllStructGetData($vtable_entry, 1)) If $id <= 0 Or $id > $lookup_table_size Then Return "" $member_name = ChrPtrToString(GetLODWORD(DllStructGetData($lookup_table, 2, $id))) If StringLeft($member_name, 1) = "@" Then Return ChrPtrToString(GetHIDWORD(DllStructGetData($lookup_table, 2, $id))) Else Return GetHIDWORD(DllStructGetData($lookup_table, 2, $id)) EndIf EndFunc ;==>IDToValue Func VTType2AutoitType($vt_type) Switch $vt_type Case $VT_I1 Return "byte" Case $VT_I2 Return "short" Case $VT_I4 Return "int" Case $VT_BSTR Return "wstr" EndSwitch EndFunc ;==>VTType2AutoitType Func AutoitType2VTType($autoit_type) Switch $autoit_type Case "byte" Return $VT_I1 Case "short" Return $VT_I2 Case "int" Return $VT_I4 Case "wstr" Return $VT_BSTR EndSwitch EndFunc ;==>AutoitType2VTType ; Find out length of string. I do not trust autoit to do this. Func wcslen($pwchar) $aCall = DllCall("ntdll.dll", "dword:cdecl", "wcslen", "ptr", $pwchar) Return $aCall[0] EndFunc ;==>wcslen Func SysAllocString($str) $aCall = DllCall("oleaut32.dll", "ptr", "SysAllocString", "wstr", $str) Return $aCall[0] EndFunc ;==>SysAllocString Func COMVariantToValue($pVariant) Local $var = DllStructCreate($tagVARIANT, $pVariant) ; Translate the vt id to a autoit dllcall type $type = VTType2AutoitType(DllStructGetData($var, "vt")) If $type = "wstr" Then $str_ptr = DllStructCreate("ptr", DllStructGetPtr($var, "data")) ; Getting random crashes when trusting autoit to automatically use right size. ; doing it myself instead (also, it should be a BSTR, but it's not. Is autoit not obeying the rules!? $str_size = wcslen(DllStructGetData($str_ptr, 1)) $data = DllStructCreate("wchar[" & $str_size + 1 & "]", DllStructGetData($str_ptr, 1)) Else $data = DllStructCreate($type, DllStructGetPtr($var, "data")) EndIf Return DllStructGetData($data, 1) EndFunc ;==>COMVariantToValue Func ValueToCOMVariant($pVariant, $vValue) $var = DllStructCreate($tagVARIANT, $pVariant) If IsInt($vValue) Then $vt_type = AutoitType2VTType("int") $var_data = $vValue ElseIf IsString($vValue) Then $vt_type = AutoitType2VTType("wstr") $var_data = SysAllocString($vValue) EndIf DllStructSetData($var, "vt", $vt_type) DllStructSetData(DllStructCreate("int", DllStructGetPtr($var, "data")), 1, $var_data) EndFunc ;==>ValueToCOMVariant ; 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($self, $refiid, $str_array, $array_size, $context, $out_array) ; It's self explainable that autoit only asks for one member $str = DllStructCreate("wchar[256]", DereferencePointer($str_array)) ConsoleWrite("AutoIt wants to look up: " & DllStructGetData($str, 1) & @CRLF) ; Autoit gave us an array with one element ready to accept the id of the member it requested. $ids = DllStructCreate("long", $out_array) ;~ ConsoleWrite("ZERO"&@CRLF) $id = FindNameInLookupTable($self, "@" & DllStructGetData($str, 1)) ConsoleWrite("ONE" & @CRLF) If $id = -1 Then $id = FindNameInLookupTable($self, DllStructGetData($str, 1)) ConsoleWrite("TWO" & @CRLF) If $id <> -1 Then DllStructSetData($ids, 1, $id) Return $S_OK Else DllStructSetData($ids, 1, $DISPID_UNKNOWN) Return $DISP_E_UNKNOWNNAME EndIf EndFunc ;==>IDispatch_GetIDsFromNames ; Create the callback so we have a pointer to this function. $IDispatch_GetIDsFromNames_Callback = DllCallbackRegister("IDispatch_GetIDsFromNames", "long", "ptr;ptr;ptr;int;int;ptr") $IDispatch_GetIDsFromNames_Callback_Ptr = DllCallbackGetPtr($IDispatch_GetIDsFromNames_Callback) ; This is called when a method is called, a property is set or get. ; This call also contains arguments returns and a lot other stuff. ; However in this trivial example we don't return anything and we don't take any arguments. Puh, could get messy Func IDispatch_Invoke($self, $dispID, $riid, $lcid, $wFlags, $pDispParams, $pVarResult, $pExceptInfo, $puArgErr) ;; Dump all parameters to console. ConsoleWrite("DispID: " & $dispID & @CRLF & "RIID: " & $riid & @CRLF & "LCID: " & $lcid & @CRLF & "wFlags: " & $wFlags & @CRLF & _ "pDispParams: " & $pDispParams & @CRLF & "pVarResult: " & $pVarResult & @CRLF & "pExceptInfo: " & $pExceptInfo & @CRLF & "puArgError: " & $puArgErr & @CRLF) $memberval = IDToValue($self, $dispID) If $memberval = "" Then Return $DISP_E_MEMBERNOTFOUND $dispparams = DllStructCreate($tagDISPPARAMS, $pDispParams) If $wFlags = BitOR($DISPATCH_METHOD, $DISPATCH_PROPERTYGET) Then If IsString($memberval) Then $self_obj = ConvertPtrToIDispatch($self) $callstring = $memberval & "($self_obj" If $pDispParams <> 0 And DllStructGetData($dispparams, "cArgs") > 0 Then Local $params[DllStructGetData($dispparams, "cArgs")] ; Fetch all arguments For $i = 0 To UBound($params) - 1 ; Save the values backwards (that's how autoit do it) $params[(UBound($params) - 1) - $i] = COMVariantToValue(DllStructGetData($dispparams, "rgvargs") + ($i * 16)) ; i*sizeof(VARIANT) $callstring &= ",$params[" & $i & "]" Next EndIf $callstring &= ")" ConsoleWrite("Calling function: " & $callstring & @CRLF) $ret = Execute($callstring) ; Set return value. ValueToCOMVariant($pVarResult, $ret) ; Give autoit the message that everything went according to plan Return $S_OK Else ValueToCOMVariant($pVarResult, COMVariantToValue($memberval)) Return $S_OK EndIf ElseIf $wFlags = $DISPATCH_PROPERTYPUT Then ;~ MsgBox(0,"","") ;~ ConsoleWrite(DereferencePointer($pDispParams+8)&@CRLF) ;~ ConsoleWrite(DllStructGetData($dispparams,"rgvargs")&@CRLF) ValueToCOMVariant($memberval, COMVariantToValue(DllStructGetData($dispparams, "rgvargs"))) Return $S_OK EndIf EndFunc ;==>IDispatch_Invoke ; Create callback $IDispatch_Invoke_Callback = DllCallbackRegister("IDispatch_Invoke", "long", "ptr;dword;ptr;dword;ushort;ptr;ptr;ptr;ptr") $IDispatch_Invoke_Callback_Ptr = DllCallbackGetPtr($IDispatch_Invoke_Callback) Func CreateObject($aMembers) ; Create a victim. Could be any COM object that inherits from IDispatch $obj_ptr = CreateIDispatchFromProgID("ScriptControl") ; Create autoit object as well. ; Hook into the object ; Offset 20 & 24 is fifth entry in vtable. Look at IDispatch and IUnknown interfaces to see why ReplaceVTableEntry($obj_ptr, 20, $IDispatch_GetIDsFromNames_Callback_Ptr) ReplaceVTableEntry($obj_ptr, 24, $IDispatch_Invoke_Callback_Ptr) ; Create space for a new bigger vtable $p = DynAlloc(4 * 7 + 4) ; sizeof(ptr)*(num entirs in dispatch)+sizeof(ptr) RelocateVTable($obj_ptr, $p, 4 * 7) ConstructLookupTable($obj_ptr, $aMembers) Return ConvertPtrToIDispatch($obj_ptr) EndFunc ; Initalize COM CoInitialize() $msgbox = MessageBox() $msgbox.flag = 64 $msgbox.title = "Important Announcement" $msgbox.text = "Object Orientation + AutoIt = True" $msgbox.Display() Func MessageBox() ; Construct a lookup table with properties and methods (methods are marked with @ Local $members[4][2] = [["@Display", "MessageBox_Display"],["flag", 0],["title", ""],["text", ""]] Return CreateObject($members) EndFunc ;==>MessageBox Func MessageBox_Display($self) Return MsgBox($self.flag, $self.title, $self.text) EndFunc ;==>MessageBox_DisplaySo what's next? Inheritance, stability, polymorphism (which the current implementation almost gives away for free. Nice!), more types (right now only ints and strings are supported) and general clean up.Stay tuned!Edit: If crashes, run again!Edit2: Replaced shell.application with ScriptControl, since shell.application would just return same old instance. Edited January 4, 2010 by monoceres Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
trancexx Posted January 4, 2010 Share Posted January 4, 2010 I was following you with the x64 compatibility (on blind ). Wanna see? ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
monoceres Posted January 4, 2010 Author Share Posted January 4, 2010 I was following you with the x64 compatibility (on blind ). Wanna see?Yes! Seeing any problems with the current implementation? I'm pretty fucking happy with it. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
ProgAndy Posted January 4, 2010 Share Posted January 4, 2010 Do you really need to create a ScriptControl-Object wouldn't it be enough to implement all funcs and then memcpy the vtable-pointer to idispatch? *GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes Link to comment Share on other sites More sharing options...
monoceres Posted January 4, 2010 Author Share Posted January 4, 2010 Do you really need to create a ScriptControl-Object wouldn't it be enough to implement all funcs and then memcpy the vtable-pointer to idispatch?You're right, I didn't even think of that. However it was way easier to use a ready to use object when developing.As long as autoit doesn't need important information from one of the methods it will work. Needing the call the real interfaces will make stuff more complicated than it needs to with function pointer call and stuff. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
James Posted January 4, 2010 Share Posted January 4, 2010 YAYAYAYAY I'm going to have some fun with this Blog - Seriously epic web hosting - Twitter - GitHub - Cachet HQ Link to comment Share on other sites More sharing options...
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