Jump to content

AutoItObject UDF


ProgAndy
 Share

Recommended Posts

This sounds amazing. :) But I don't understand so much of it. :)

If the system wide object can have methods is there a copy of the AutoIt inerpreter included in the object?

Can the object continue to exist when the script which created is is closed?

How does this protect the AutoIt source or is that not what you meant?

Does this mean that a 32 bit script could create an object that could be accessed by a 64 bit process and visa versa?

Registered objects run in their own memory space. Still you are accessing them like they are in yours (pay attention to Autoit's COM pointer type bug).

Object is managed by the server, script that registers it. When the server is closed the object is gone.

Yes, bitness is irrelevant.

It's like with any other COM server (exe version).

...probably some examples would help.

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

Here's one example. Server script (compiled to a3x form):

COMServerExample.zip

... unzip it.

Then run this code from the same folder:

#include "AutoItObject.au3"

Opt("MustDeclareVars", 1)

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


; Initialize AutoItObject
_AutoItObject_Startup()


; GUI
Global $hGui = GUICreate("Cool thing 2")
Global $hButton = GUICtrlCreateButton("Call some method", 100, 70, 100, 30)
Global $hButton2 = GUICtrlCreateButton("Call other method", 100, 170, 100, 30)
GUISetState()

Global $oObject = _AutoItObject_ObjCreateEx(@ScriptDir & "\COMServerExample.a3x", "cbi:MyServer.Application")
; Use it to store some data
$oObject.Storage = FileRead(@ScriptFullPath)

While 1
    Switch GUIGetMsg()
        Case -3
            ExitLoop
        Case $hButton
            $oObject.Hey()
        Case $hButton2
            $oObject.ShowStored()
    EndSwitch
WEnd

$oObject.Quit ; Instruct server to exit.

You are using object from the a3x without any kind of "#include" statement or whatever.

Source of that a3x is (for interested):

#include "AutoitObject.au3"

Opt("MustDeclareVars", 1)

If Not StringRegExp($CmdLineRaw, "(?i)(-|/)StartServer\b", 0) Then Exit


; Error monitoring
Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")
Func _ErrFunc()
EndFunc

; Initialize AutoItObject
_AutoItObject_StartUp()

; Create Object
Global $oObject = _SomeObject()

; Register it globaly with some identifier
Global $sMyCLSID = "MyServer.Application"
Global $hObj = _AutoItObject_RegisterObject($oObject, $sMyCLSID)

_AutoItObject_AddProperty($oObject, "ROT", $ELSCOPE_PUBLIC, $hObj)


While 1
    Sleep(100)
WEnd

_AutoItObject_UnregisterObject($hObj)
$oObject = 0

; Define object
Func _SomeObject()
    Local $oClassObject = _AutoItObject_Class()
    $oClassObject.Create()
    $oClassObject.AddMethod("Hey", "_Obj_MsgBox")
    $oClassObject.AddMethod("Quit", "_QuitObject")
    $oClassObject.AddMethod("ShowStored", "_Obj_ShowStored")
    $oClassObject.AddProperty("Title", $ELSCOPE_PUBLIC, "Something")
    $oClassObject.AddProperty("Text", $ELSCOPE_PUBLIC, "Some text")
    $oClassObject.AddProperty("Flag", $ELSCOPE_PUBLIC, 64 + 262144)
    $oClassObject.AddProperty("Storage")
    Return $oClassObject.Object
EndFunc   ;==>_SomeObject

Func _Obj_MsgBox($oSelf, $sTitle = "")
    If $sTitle Then Return MsgBox($oSelf.Flag, $sTitle, $oSelf.Text)
    MsgBox($oSelf.Flag, $oSelf.Title, $oSelf.Text)
EndFunc   ;==>_Obj_MsgBox

Func _Obj_ShowStored($oSelf)
    MsgBox(64 + 262144, "Stored", $oSelf.Storage)
EndFunc

Func _QuitObject($oSelf)
    _AutoItObject_UnregisterObject($oSelf.ROT)
    $oObject = 0
    Exit
EndFunc

New 1.2.4.0 version of AutoItObject is required for that. This version added new functionality to _AutoItObject_ObjCreateEx function to avoid wrappers.

It's the closest thing to DLL compilation of AutoIt scripts that's possible.

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

@trancexx

As it seems VBScript uses the GetObject to read the objects from the ROT table.

http://www.ikriv.com/en/prog/info/dotnet/ExeAutomation/index.html

When reading more on the internet .NET seems be able to handle this as well.

Thanks

rgds

ptrex

Link to comment
Share on other sites

Hi, I'm having a problem destructing an object, I am not sure if AutoItObj is suppose to do this. Please have a look.

#Include <AutoItObject.au3>

_AutoItObject_StartUp()

$oObject = _Autoitobject_Create()
_Autoitobject_AddMethod( $oObject, "RunMe", "RunMe")
_AutoItObject_AddDestructor($oObject, "Destructor")

$oObject.RunMe()
ConsoleWrite("== Sleep ==" & @CRLF)
Sleep(1000)
$oObject = 0

Func RunMe(ByRef $oSelf)
    ConsoleWrite("runnung RunMe" & @CRLF)
    $oSelf = 0   ;<== didn't call the Destructor
;~  $oObject = 0 ;<== this works fine.

    ConsoleWrite("Is $oSelf = $oObject ?" & ($oSelf = $oObject) & @CRLF) ;<== what's their difference?
EndFunc

Func Destructor($oSelf)
    ConsoleWrite("runnung Destruct" & @CRLF)
EndFunc
Link to comment
Share on other sites

even without ByRef, only $oObject will trigger the destructor

Edit: updated code...

#Include <AutoItObject.au3>

_AutoItObject_StartUp()

$oObject = _Autoitobject_Create()
_Autoitobject_AddMethod( $oObject, "RunMe", "RunMe")
_AutoItObject_AddDestructor($oObject, "Destructor")

$oObject.RunMe()
Sleep(1000)
ConsoleWrite("== Sleep ==" & @CRLF) ;<== the destructor should run before this
$oObject = 0

Func RunMe($oSelf)
    ConsoleWrite("runnung RunMe" & @CRLF)
    $oSelf = 0   ;<== didn't call the Destructor
;~  $oObject = 0 ;<== this works fine.
EndFunc

Func Destructor($oSelf)
    ConsoleWrite("runnung Destruct" & @CRLF)
EndFunc
Edited by MiserableLife
Link to comment
Share on other sites

As it seems VBScript uses the GetObject to read the objects from the ROT table.

http://www.ikriv.com/en/prog/info/dotnet/ExeAutomation/index.html

When reading more on the internet .NET seems be able to handle this as well.

I have made a demo project in C# that can call methods on the object created by ROT server. The hard part was getting the object from the ROT table. Once we have the correct System.__ComObject, invoking members is as simple as:

object myAu3Object = ROTHelper.GetActiveObject("{D07F2CEA-696F-47CD-99A9-D31E3641169B}");
Type myAu3Type = myAu3Object.GetType();

myAu3Type.InvokeMember("Text", BindingFlags.SetProperty, null, myAu3Object, new object[] { "Some cool text" });
myAu3Type.InvokeMember("MsgBox", BindingFlags.InvokeMethod, null, myAu3Object, new object[] { "A cool title" });

You can download the demo project here: http://dl.dropbox.com/u/10628810/ROTFun.zip

I tried for a moment to use dynamic keyword from the 4.0 framework which apparently can use IDispatch to do runtime method binding. But this did not work out-of-the-box which I don't really understand. I will have to look into this later, because I haven't much experience with this at all. Do objects created in this way implement IDispatch and expose it even when accessed via ROT?

This is the au3 I used to test (by trancexx):

#include "AutoitObject.au3"

Opt("MustDeclareVars", 1)

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

; Initialize AutoItObject
_AutoItObject_StartUp()

; Create Object
Global $oObject = _SomeObject()

; Register it globaly with some identifier
Global $sMyCLSID = "{D07F2CEA-696F-47CD-99A9-D31E3641169B}"
_AutoItObject_RegisterObject($oObject, $sMyCLSID)

; GUI to keep the script alive
Global $hGui = GUICreate("Cool thing")
Global $hButton = GUICtrlCreateButton("Click", 100, 100, 100, 30)

GUISetState()

While 1
    Switch GUIGetMsg()
        Case -3
            ExitLoop
        Case $hButton
            $oObject.MsgBox() ; call to test
    EndSwitch
WEnd

; Define object
Func _SomeObject()
    Local $oClassObject = _AutoItObject_Class()
    $oClassObject.Create()
    $oClassObject.AddMethod("MsgBox", "_Obj_MsgBox")
    $oClassObject.AddProperty("Title", $ELSCOPE_PUBLIC, "Something")
    $oClassObject.AddProperty("Text", $ELSCOPE_PUBLIC, "Some text")
    $oClassObject.AddProperty("Flag", $ELSCOPE_PUBLIC, 64 + 262144)
    $oClassObject.AddDestructor("_DestructorForSomeObject")
    Return $oClassObject.Object
EndFunc   ;==>_SomeObject

Func _Obj_MsgBox($oSelf, $sTitle = "")
    If $sTitle Then Return MsgBox($oSelf.Flag, $sTitle, $oSelf.Text)
    MsgBox($oSelf.Flag, $oSelf.Title, $oSelf.Text)
EndFunc   ;==>_Obj_MsgBox

Func _DestructorForSomeObject($oSelf)
    ConsoleWrite("!...destructing..." & @CRLF)
EndFunc   ;==>_DestructorForSomeObject

Edit: At request (ptrex), I also provide the same code in VB.NET. This is my first program in VB.NET ever so it could be a little more buggy than the C# version. :) The ROTHelper class is also translated to VB.NET and can be found in the download link above.

Dim myAu3Object As Object = ROTHelper.GetActiveObject("{D07F2CEA-696F-47CD-99A9-D31E3641169B}")
Dim myAu3Type As Type = myAu3Object.[GetType]()

myAu3Type.InvokeMember("Text", System.Reflection.BindingFlags.SetProperty, Nothing, myAu3Object, New Object() {"Some cool text"})
myAu3Type.InvokeMember("MsgBox", System.Reflection.BindingFlags.InvokeMethod, Nothing, myAu3Object, New Object() {"A cool title"})
Edited by Manadar
Link to comment
Share on other sites

Excellent, excellent!

I tried for a moment to use dynamic keyword from the 4.0 framework which apparently can use IDispatch to do runtime method binding. But this did not work out-of-the-box which I don't really understand. I will have to look into this later, because I haven't much experience with this at all. Do objects created in this way implement IDispatch and expose it even when accessed via ROT?

Yes, IDispatch. All the calls should be done thru IDispatch's Invoke. If some framework possibly relies on ITypeInfo (GetTypeInfo call) to get informations and do the calling upon them, then it wouldn't work because these objects are set to return E_NOTIMPL when that happens. Maybe (only maybe) that's the case there. Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

Yes, IDispatch. All the calls should be done thru IDispatch's Invoke. If some framework possibly relies on ITypeInfo (GetTypeInfo call) to get informations and do the calling upon them, then it wouldn't work because these objects are set to return E_NOTIMPL when that happens. Maybe (only maybe) that's the case there.

That's it. I was getting a NotImplementedException in my code.
Link to comment
Share on other sites

GetTypeInfo doesn't seem too hard to implement (at least a very basic one with CreateDispTypeInfo). Maybe I'll try it later.

Edit: There's just one problem with PARAMDATA. I don't know how to fill it.

Edited by ProgAndy

*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

GetTypeInfo doesn't seem too hard to implement (at least a very basic one with CreateDispTypeInfo). Maybe I'll try it later.

Edit: There's just one problem with PARAMDATA. I don't know how to fill it.

This is one of the funniest things I read lately. Monty Python style.

Thank you for that. :)

That's the beauty of our AutoItObjecs objects, they are incredibly non-static (wrong term surely).

You could add new function for describing the object with passed dtag. For wrapped objects there shouldn't be problems to add type information.

...And yes, I'm still laughing :)

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

That's the beauty of our AutoItObjecs objects, they are incredibly non-static (wrong term surely).

This feature allows to design new name. Let's call it like a comlet. Compare with scriptlet:

scriptlet

In JavaServer Pages (JSP) technology, a scriptlet is a piece of Java-code embedded in the HTML-like JSP code.

comlet:

In AutoIt3 technology, a comlet is a COM server embedded in Windows on fly.

:unsure:

The point of world view

Link to comment
Share on other sites

  • 2 months later...

I've been trying to get my head around this and I don't think I get it.

I get errors in Scite about $this although it does seem to run and also $Bitmap.Bitmap should show a handle but is always blank and there is a com error related to it.

>"C:\Program Files\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /prod /ErrorStdOut /in "D:\My Documents\AutoITscripts\AutoItObject Package 1.2.8.2\GDIPlusObjectWrapper.au3" /autoit3dir "C:\Program Files\AutoIt3" /UserParams

+>10:55:14 Starting AutoIt3Wrapper v.2.0.1.24 Environment(Language:0409 Keyboard:00000452 OS:WIN_7/Service Pack 1 CPU:X64 OS:X86)

>Running AU3Check (1.54.19.0) params:-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 from:C:\Program Files\AutoIt3

D:\My Documents\AutoITscripts\AutoItObject Package 1.2.8.2\GDIPlusObjectWrapper.au3(67,19) : WARNING: $this: declared, but not used in func.

Func _NewBMP($this,

~~~~~~~~~~~~~~~~~~^

D:\My Documents\AutoITscripts\AutoItObject Package 1.2.8.2\GDIPlusObjectWrapper.au3 - 0 error(s), 1 warning(s)

->10:55:14 AU3Check ended.rc:1

>Running:(3.3.6.1):C:\Program Files\AutoIt3\autoit3.exe "D:\My Documents\AutoITscripts\AutoItObject Package 1.2.8.2\GDIPlusObjectWrapper.au3"

! COM Error ! Number: 0x00000005 ScriptLine: 75 - Conversion of parameters failed

Destroy Bitmap

+>10:55:17 AutoIT3.exe ended.rc:0

>Exit code: 0 Time: 4.958

Could you tell me what I've done wrong so I can try and understand how to use it.

Thanks

#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

#include "AutoItObject.au3"
#include <GDIPLus.au3>

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


Opt("MustDeclareVars", 1)

_GDIPlus_Startup()
_AutoItObject_StartUp()

Global $Bitmap = _BMP_NewBMP()
$Bitmap.Open ("D:\Back2.bmp")

With $Bitmap
    MsgBox(0,"Bitmap",    .Bitmap & @CRLF _
                        & .Width & @CRLF _
                        & .Height)
EndWith

$Bitmap = 0

_GDIPlus_Shutdown()
_AutoItObject_Shutdown()





Func _BMP_NewBMP()

    Local $object = _AutoItObject_Create()
    _AutoItObject_AddProperty($object, "originalPath", $ELSCOPE_PRIVATE)
    _AutoItObject_AddProperty($object, "hBitmap", $ELSCOPE_PRIVATE,0)
    _AutoItObject_AddProperty($object, "imageWidth", $ELSCOPE_PRIVATE,0)
    _AutoItObject_AddProperty($object, "imageHeight", $ELSCOPE_PRIVATE,0)

    _AutoItObject_AddMethod($object, "Open", "_NewBMP")
    _AutoItObject_AddMethod($object, "Path", "_BMP_GetPath")
    _AutoItObject_AddMethod($object, "Bitmap", "_BMP_GetHandle")
    _AutoItObject_AddMethod($object, "Width", "_BMP_GetWidth")
    _AutoItObject_AddMethod($object, "Height", "_BMP_GetHeight")

    _AutoItObject_AddDestructor($object, "_BitmapDestroy")
    Return $object

EndFunc

Func _NewBMP($this, $sFileName)

    If NOT FileExists($sFileName) then Return 0
    Local $BMP = _GDIPlus_BitmapCreateFromFile($sFileName)

    $this.imageWidth = _GDIPlus_ImageGetWidth($BMP)
    $this.imageHeight = _GDIPlus_ImageGetHeight($BMP)
    $this.originalPath = $sFileName
    $this.hBitmap = $BMP

EndFunc

Func _BitmapDestroy($this)

    ConsoleWrite("Destroy Bitmap" & @CRLF)
    _GDIPlus_BitmapDispose($this.Bitmap)

EndFunc

Func _BMP_GetPath($this)
    Return $this.originalPath
EndFunc

Func _BMP_GetWidth($this)
    Return $this.imageWidth
EndFunc

Func _BMP_GetHeight($this)
    Return $this.imageHeight
EndFunc

Func _BMP_GetHandle($this)
    Return $this.hBitmap
EndFunc
Edited by ChrisL
Link to comment
Share on other sites

No.

There are some exceptions. I have seen some UDFs calling IsPtr on the handle and therefore failing if the handle is converted top a number before. I cannot remember them, but if you ever find one, use Ptr($handle) and/or ask for an update of the UDF without this check.

*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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...