COM Extensions to AutoIt

A short introduction

What is COM?

COM stands for "Component Object Model". It is the Microsoft way to interconnect software using a common interface. These interfaces are defined in a COM Object.

Before COM, you had to know the exact implementation of a program before you could 'interface' with it. Using COM, you can now "talk" to its defined Object(s). The only things you have to know are the names of the Objects that are used and which 'properties' or 'methods' they have.

What are (Object) properties or methods?

These are the two basic characteristics of an Object. You can see a 'property' as the data storage of an Object. A 'method' can be seen as an internal function call to do something with the data.

Do I need COM in my AutoIt script?

It just depends. AutoIt has a lot of built-in functions and a huge library of UDF's. You can do most of your programming with these. However if you need special 'interfacing' to other applications, using COM might save you some lines of script. Scripters have to be aware that the existence of COM Objects depend HEAVILY on the Operating System AND the installed software. The examples below have all been tested under a 'plain' Windows XP professional version with Microsoft Office 2000.

An example usage of COM in AutoIt

Let's say you want to minimize all open windows. You could do this using regular AutoIt functions like WinList and WinSetState. However, two lines of COM-code can give you the same result:

Local $oShell = ObjCreate("shell.application")

$oShell.MinimizeAll

Side note: The example is no longer the shortest way to minimize all windows after the introduction of the WinMinimizeAll() function in AutoIt.

 

On the first line we create a new object called "shell.application". This is an internal Windows object, defined in shell32.dll. The pointer to this new object is assigned to the variable $oShell. $oShell is from now on an Object variable.

On the second line, we use a Method called "MinimizeAll" to the oShell object. This will minimize all windows.

All Windows versions have a huge amount of internal Objects for various purposes. And applications like Excel or Word have also their own set of Objects.

However, it is sometimes difficult to get a list of all existing Objects defined on your system with their corresponding properties and methods. Searching at Microsoft.com or Google.com might give you some clues about the Object 'X' you want to use.

For instance, you can find information about the "shell.application" object at:
http://msdn.microsoft.com/en-us/library/bb774094.aspx


To get a peek on all objects currently installed on your system, the "OLE/COM Object Viewer" is a very helpful tool. This tool will be explained in a separate section below.

Let's do another example. We would like to get a HTML source code from a certain web page. You could use the internal InetGet() function to save the result to a file and retrieve it back again with FileRead(). But these lines of code do the same:

Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1")
$oHTTP.Open("GET", "http://www.AutoItScript.com")
$oHTTP.Send()
$HTMLSource = $oHTTP.Responsetext

The (string) variable $HTMLSource now contains the complete HTML code of the AutoItScript.com home page (that is, the top HTML-Frame).

(Information about the "winhttp.winhttprequest" object can be found at:
http://msdn.microsoft.com/en-us/library/aa384106.aspx )

 

Please mind this: The existence of Objects depend on the computer's operating system and installed programs. For example, the winhttp.winhttprequest.5.1 object only exists on computers that have at least Internet Explorer version 5 installed. When you are sharing scripts that use COM objects, be sure the objects exist on all computers.

Object variables behave a bit different than other types of AutoIt variables. An Object is not a real value, but a 'pointer' to something outside the script. So you can't perform arithmetic's, nor equations on Object variables. When you assign an Object variable a different value, the 'pointer' will automatically be released. You can, for instance, force deletion of an Object by assigning it any number or any text value.

Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1") ; Object is created
$oHTTP = 0 ; Object is deleted

You don't need to delete Objects when you are finished. If a script exits, AutoIt tries to release all active references to Objects that had been created in the script. Same thing happens when you had defined a local Object variable inside a function, and the function ends with a return.

 

Automation using COM

A very popular application of COM is to 'automate' programs. Instead of using the regular AutoIt functions like Send() or WinActivate(), you can make use of the Objects that are defined inside the program.

Here is an example that 'automates' Microsoft Excel:

Local $oExcel = ObjCreate("Excel.Application") ; Create an Excel Object
$oExcel.Visible = 1 ; Let Excel show itself
$oExcel.WorkBooks.Add ; Add a new workbook
$oExcel.ActiveWorkBook.ActiveSheet.Cells(1, 1).Value = "Text" ; Fill a cell
Sleep(4000) ; Display the results for 4 seconds
$oExcel.ActiveWorkBook.Saved = 1 ; Simulate a save of the Workbook
$oExcel.Quit ; Quit Excel

The complexity of controlling other programs depends on that specific program, not on the AutoIt script. If something does not work as expected you might need to consult the applications' documentation and not the AutoIt help.

 

Special Statements

In AutoIt, two special statements are designed for COM usage:

With/EndWith and the For/In/Next loop.

With..EndWith

The With/EndWith statement does not add functionality, but it makes your script easier to read. For instance, the previous example using Excel can also be written as:

Local $oExcel = ObjCreate("Excel.Application") ; Create an Excel Object

With $oExcel
    .Visible = 1 ; Let Excel show itself
    .WorkBooks.Add ; Add a new workbook
    .ActiveWorkBook.ActiveSheet.Cells(1, 1).Value = "Text" ; Fill a cell
    Sleep(4000) ; Display the results for 4 seconds
    .ActiveWorkBook.Saved = 1 ; Simulate a save of the Workbook
    .Quit ; Quit Excel
EndWith

This example does not save you a lot of text, but when your object uses long lines of properties/methods, you can shorten it heavily within a With statement.

For..In

The FOR...IN loop is required when using Collections. Collections are a special type of Object, that exist out of multiple sub-Objects. You could see them as Arrays (Actually, the For..In statement also works on Array-type variables).

For..In loop using an Array

Below is an example of an For..In loop. This example uses a normal AutoIt array, so it has nothing to do with COM. It's just to show you the principle:

#include <MsgBoxConstants.au3>

Local $sString = "" ; A string for displaying purposes

Local $aArray[4]
$aArray[0] = "A" ; We fill an array
$aArray[1] = 0 ; with several
$aArray[2] = 1.3434 ; different
$aArray[3] = "Example Text" ; example values.

For $iElement In $aArray ; Here it starts...
    $sString &= $iElement & @CRLF
Next

; Display the results
MsgBox($MB_OK, "For..In Array Example", "Result: " & @CRLF & $sString)

For..In loop using an Object

In most cases you can't use 'normal' Object methods to retrieve the elements of a collection. In 'COM'-terms they say you have to 'enumerate' them. This is where the For..In loop comes in.

The Excel example below loops on cells A1:O16 on the current active sheet. If one of the cells has a value less than 5, the code replaces the value with 0 (zero):

Local $oExcel = ObjCreate("Excel.Application") ; Create an Excel Object

$oExcel.Visible = 1 ; Let Excel show itself
$oExcel.WorkBooks.Add ; Add a new workbook

Local $aArray[16][16] ; These lines
For $i = 0 To 15 ; are just
    For $j = 0 To 15 ; an example to
        $aArray[$i][$j] = $i ; create some
    Next ; cell contents.
Next

$oExcel.activesheet.range("A1:O16").value = $aArray ; Fill cells with example numbers

Sleep(2000) ; Wait a while before continuing

For $iCell In $oExcel.ActiveSheet.Range("A1:O16")
    If $iCell.Value < 5 Then
        $iCell.Value = 0
    EndIf
Next

$oExcel.ActiveWorkBook.Saved = 1 ; Simulate a save of the Workbook
Sleep(2000) ; Wait a while before closing
$oExcel.Quit ; Quit Excel

 

Advanced COM usage

The following features of AutoItCOM requires profound knowledge of COM Events and COM Error handling.

If you are a newbie to COM programming, please read some good COM documentation first.

 

The bible of COM is the book called "Inside OLE 2" by Kraig Brockschmidt (Microsoft Press).

 

You can also find some COM resources on the internet (not AutoIt related):

http://msdn.microsoft.com/en-us/library/ms694363.aspx (introduction)

http://www.garybeene.com/vb/tut-obj.htm (about Objects in Visual Basic)

http://java.sun.com/docs/books/tutorial/java/concepts/ (Using objects in Java)

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686915.aspx (Object Events in C++)

http://www.garybeene.com/vb/tut-err.htm (Error handling in Visual Basic)

COM Events

Normal COM Automation mainly uses one-way communication. You 'ask' the Object for any properties or results from a Method. However a COM Object can also 'talk back' to your script when it suits it.

This could be very handy in case you need to wait for some COM-related action to happen.

Instead of writing a kind of loop, asking the Object if something interesting has happened, you can let the Object itself call a specific UDF in your script. Meanwhile you can do other things in your script (almost) simultaneously.

 

Not all Object to support events. You have to read the Object documentation carefully whether it supports events or not.

If it does, the second thing to know is the type of Events it supports. AutoItCOM can only receive 'dispatch' type events.

Finally you have to know the names of the Events the Object could generate, including their arguments (if any).

 

Only when you have all this information, you can start building an AutoIt script using COM Events.

Below is a snippet from a script that demonstrates how to receive Events from the Internet Explorer:

Local $oIE = ObjCreate("InternetExplorer.Application.1") ; Create an Internet Explorer Object

Local $oEventObject = ObjEvent($oIE, "IEEvent_", "DWebBrowserEvents") ; Start receiving Events.

$oIE.url = "http://www.autoitscript.com" ; Load an example web page
; From now on, the $oIE Object generates events during web page load.
; They are handled in the event functions shown below.

; Here you can let the script wait until the user wants to finish.
...(your code here)...

$oEventObject.stop ; Tell IE we want to stop receiving Events
$oEventObject = 0 ; Kill the Event Object
$oIE.quit ; Quit IE
$oIE = 0 ; Remove IE from memory (not really necessary)
Exit ; End of main script

; A few Internet Explorer Event Functions.
;
; For the full list of IE Event Functions, see the MSDN WebBrowser documentation at:
; http://msdn.microsoft.com/en-us/library/aa768400.aspx

Func IEEvent_StatusTextChange($sText)
    ; In the complete script (see link below) we show the contents in a GUI Edit box.
    GUICtrlSetData($GUIEdit, "IE Status text changed to: " & $sText & @CRLF, "append")
EndFunc   ;==>IEEvent_StatusTextChange

Func IEEvent_BeforeNavigate($sURL, $iFlags, $sTargetFrameName, $dPostData, $sHeaders, $bCancel)
    ; In the complete script (see link below) we show the contents in a GUI Edit box.
    ; Note: the declaration is different from the one on MSDN.
    GUICtrlSetData($GUIEdit, "BeforeNavigate: " & $sURL & " Flags: " & $iFlags & @CRLF, "append")
EndFunc   ;==>IEEvent_BeforeNavigate

Click here to view the complete script.

The main line in this script is: $EventObject = ObjEvent ($oIE, "IEEvent_", ...).
This function takes the object $oIE and reroutes it's events to AutoIt functions whose names start with IEEvent_. The third parameter is optional. It is used when an Object has multiple Event interfaces and you don't want AutoIt to choose one automatically.

The Object responsible for the continuous rerouting is $EventObject. This variable does not require any further attention, unless you want to stop the events.

To stop rerouting Events, you can not just delete the variable like $EventObject = "". The reason is that the 'calling' Object is still holding a reference to this variable, and it won't loose it until the Object itself quits. You could solve this problem by killing the 'calling' Object, but you can also tell the Object that you don't want to receive any events by using: $EventObject.Stop. Then you can (but not really necessary) kill the Event by assigning it any value, like: $EventObject = ""

If you know the names of the Events $oIE fires, you can implement the Events you are interested in by creating AutoIt UDF's with the name IEEvent_Eventname(optional arguments). Be sure you use the correct number of arguments and in their correct order as specified for THAT Event function. Otherwise you might end up with unexpected values.

You don't have to implement ALL event functions. Those not implemented will just be ignored.

More script examples using COM Event functions can be found via this URL.

 

Limitations on COM Events in AutoIt

Some Objects (like the 'WebBrowser') pass arguments to their Event Functions 'by reference'. This is intended to allow the user change these arguments and passing it back to the Object. However, AutoIt uses its own variable scheme, which is not compatible to COM variables. This means that all values from Objects need to be converted into AutoIt variables, thus losing the reference to the original memory space. Maybe in the near future we can solve this limitation for you !

COM Error Handling

Using COM without proper error handling can be very tricky. Especially when you are not familiar with the Objects in your script.

Only if there is no way to prevent a COM error, you could install an "Error Handler" in which you take action after the error has happened. It is not a solution to make a buggy script work properly. Neither does it catch non-COM related script errors (e.g. declaration and syntax errors).

Error handling is implemented in the same way as a normal COM Event, using ObjEvent() and a user defined COM Event Function. The only difference is the usage of the fixed string "AutoIt.Error" as the name of the object.

An example:

#include <MsgBoxConstants.au3>

; Install a custom error handler
; if used in a Function, the handler is automatically disable on return.
; If not installed the script will terminated on error
Local $oMyError = ObjEvent("AutoIt.Error", "ErrFunc")

; Perform a deliberate failure (the object doesn't exist)
Local $oIE = ObjCreate("InternetExplorer.Application")
$oIE.visible = 1
$oIE.bogus
If @error Then
    ; checking @error need a custom error handler
    MsgBox($MB_OK, "Error", "There was an error on the previous line.")
EndIf

MsgBox($MB_OK, "", "Script exit") ; this line will not be executed if no handler is installed

Exit

; This is a custom error handler
Func ErrFunc($oError)
    MsgBox($MB_OK, "We intercepted a COM Error !", _
            "Number: 0x" & Hex($oError.number, 8) & @CRLF & _
            "Description: " & $oError.windescription & _
            "At line: " & $oError.scriptline)
EndFunc   ;==>ErrFunc

 

One thing is special about the Error Event Handler, and that is the Object it returns. This is an AutoIt Error Object that contains some useful properties and methods. It's implementation is partly based on the "Err" Object in VB(script):

Properties of the AutoIt Error Object:

.number The Windows HRESULT value from a COM call
.windescription The FormatWinError() text derived from .number
.source Name of the Object generating the error (contents from ExcepInfo.source)
.description Source Object's description of the error (contents from ExcepInfo.description)
.helpfile Source Object's helpfile for the error (contents from ExcepInfo.helpfile)
.helpcontext Source Object's helpfile context id number (contents from ExcepInfo.helpcontext)
.lastdllerror The number returned from GetLastError()
.scriptline The script line on which the error was generated

 

A note for UDF writers

You can have as many COM Error event handlers as you like. The last registered and alive object is the one that gets called.

However, you can never stop an existing Error Handler without releasing the variable it had been assigned to. That happens when that variable goes out of scope or gets reassigned.

OLE/COM Object Viewer

The "OLE/COM Object Viewer" is a very handy tool to get a peek on all COM objects currently installed on your system. It is part of the Windows Server 2003 resource kit (OK for Windows XP and above) and can be downloaded for free from: http://www.microsoft.com/en-us/download/details.aspx?id=17657

The setup of this program is a bit awkward. It will not create any start menu icon for you. Instead, a file called oleview.exe will be installed in the C:\Program Files\Resource Kit directory (default install).

When running oleview.exe, some systems will complain about a missing file called iviewers.dll. This file is required, but strangely enough not included in the latest setup. You can obtain this dll from an older version of oleview.exe at: http://download.microsoft.com/download/2/f/1/2f15a59b-6cd7-467b-8ff2-f162c3932235/ovi386.exe. It will install the files by default to the C:\MSTOOLS\BIN directory. You only need the file iviewer.dll. Copy it to the same directory where oleview.exe resides, then register the dll using the command line: regsvr32 iviewers.dll.

Let's do an example with the Oleviewer. Run it and follow this tree: Object Classes->Grouped by Component Category->Control->Microsoft Web Browser.

 

 

In the left-hand pane you see all the COM Interfaces that have been defined for this object. We talk about those later. Take a closer look at the right column. It contains a lot of information about this object that can be useful in an AutoIt script. Most important is the "VersionIndependentProgID". This is the name to be used in an ObjCreate(), ObjGet() or ObjEvent(). Furthermore it contains the directory and filename that contains the object. This can be an EXE, a DLL or an OCX file. InProcServer32 means that the object runs in the same thread as your script (in-process). When you see LocalServer32, the object runs as a separate process. The object must also contain a type library (the lines following "TypeLib="), otherwise it can't be used in an AutoIt script.

The interfaces in the left-hand pane provide various ways of interacting with the object. Some are used for storage (IStorage, IPersist), others for embedding in a GUI (IOleObject, IOleControl). AutoIt uses the IDispatch interface for automation. This interface 'exposes' all scriptable methods and properties that the object supports. If there is no IDispatch interface for an object, you can't use the object in an AutoIt script.

Let's take a look at this interface. Right-click on the name IDispatch and choose "View..." from the context menu. Then click the "View TypeInfo..." button. (Note: if this button is greyed out, you failed to register the iviewers.dll file -- see above., or the object does not have a type library)

 

 

The "ITypeInfo Viewer" window shows all the information that has been provided for the object; it may not be complete. If the developer decides not to include a help file, you will only see the names of the method/properties and nothing else. The "Microsoft Web Browser" type library is however quite extensive. Just click an item in the left-hand pane, and a description will be shown at the right. Sometimes you have to browse through "Inherited Interfaces" to retrieve more methods for the object.

The syntax of the described methods/properties are in C/C++ style. A property described as "HRESULT Resizable([in] VARIANT_BOOL pbOffline)", has to be rewritten in AutoIt like: $Resizable=$Object.Resizable ($Object holds the object created with ObjCreate or ObjGet).

COMmonly mixed up terms

These terms are commonly mixed up with COM, but have a different meanings:

OOP = Object Oriented Programming. A programming technique in which software components are put together from reusable building blocks known as Objects.

DDE = Dynamic Data Exchange.

You can say this is the predecessor of COM. It used IPC to transfer information and commands between different applications.

OLE = Object Linking and Embedding

In its first version, OLE was an extended version of DDE to 'embed' data from one program into another. The current generation of OLE is built on top of COM and is part of ActiveX.

Automation = This is a way of manipulating another application's objects. It is used in OLE, ActiveX and COM.

ActiveX = The next generation OLE with Automation, at first mainly developed to interface between applications over a network (especially web browsers). ActiveX is built on top of COM.

DCOM = Distributed COM. A slight modification to COM, making it able to communicate between different physical computers.

.NET (dot Net) = This is not really a piece of software, but an 'idea' from Microsoft to interconnect just about "everything" through (their) software. "dot Net" is used mainly for Web-based services.

s