COM Extensions to AutoIt

A short introduction

What is COM?

COM stands for Component Object Model. It is the Microsoft way of interconnecting software written in various languages using common interfaces. 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 and methods they have.

What are (Object) properties and methods?

They 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 many built-in functions and a huge library of UDFs. You can do most of your programming with these functions; 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 version of the operating system and the version of the installed software. The examples below have been tested on a PC running Windows XP Professional. and for the Excel example, with Microsoft Office 2000.

An example of using COM in AutoIt

Let's say you want to minimize all open windows. You could do this using the built-in AutoIt functions WinList() and WinSetState(); however, this can also be done with two lines of COM code:

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.

 

In the first line we give our script access to shell.application. This is an internal Windows object, defined in shell32.dll. The pointer to this object is assigned to the variable $oShell. $oShell is from now on an Object variable.

In the second line, we use a method of shell.application called MinimizeAll. This will minimize all windows.

All Windows versions have numerous internal objects for various purposes; applications such as Excel and Word have also their own sets of objects.

However, it is sometimes difficult to get a list of all objects defined on your system with their corresponding properties and methods. Searching at Microsoft.com or Google.com might give you some clues about an object you wish to use. For example, 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 consider another example: we would like to get the HTML source code from a certain web page. You could use the built-in InetGet() function to save the result to a file and then retrieve it with FileRead(). But these lines of code achieve this objective without creating a file:

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 be aware: Whether particular objects are present on a computer depends on its 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 the computers on which your script will be run.

Object variables behave a bit differently from other types of AutoIt variables. An object variable contains not a real value, but a pointer to something outside the script. So you can't perform calculations 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 have to delete objects when you are finished with them. When a script terminates, AutoIt tries to release all active references to objects that have been created by the script.

Automation using COM

A very popular application of COM is to automate programs. Instead of using built-in AutoIt functions such as Send() or WinActivate(), you can make use of the objects that are defined inside the program you are controlling.

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 documentation of the applications, 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 much typing, but when properties and/or methods of an object are used in many lines of code, you can shorten the code signifcanty, and make it easier to read, with With..Endwith statements.

For..In..Next

The For...In..Next loop is used with collections. Collections are a convenient way to see a related group of items as a single object. For example, the leaves on a tree can be seen as a collection, with the tree as the parent object. AutoIt's maps are collections, and arrays can be traversed with For...In.

For..In,,,Next loop using an array

Below is an example of an For..In loop. This example uses an 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..Next loop using an Object

In most cases to access the elements of a collection, a special technique is required: in COM terminology, you have to enumerate them. This is where the For..In..Next loop comes in.

The Excel example below loops through 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):

COM Events

Basic COM Automation mainly uses one-way communication: you ask an object for the value of a property, or you call a method to obtain a result. But some COM objects can speak to your script in some situations. This can be very handy when you need to wait for some COM-related action to happen.

Instead of coding a loop, asking in each iteration if something interesting has happened, you can get the object to call a specific function in your script. Meanwhile you can do other things in your script (almost) simultaneously.

There are several steps to take before coding recognition of COM events in your script:

Only when you have performed these steps 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 key line in this script is: $EventObject = ObjEvent ($oIE, "IEEvent_", ...). This function takes the object $oIE and routes its events to the 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 variable responsible for the continually routing is $EventObject. This variable does not require any further attention, unless you want to stop flow of events.

To stop routing events to your script, you can not just delete the variable, as in $EventObject = "". The reason is that the calling Object is still holding a reference to this variable, and it won't lose it until the pointer to the object itself is deleted. 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 any more by coding: $EventObject.Stop. Then you can kill the events by assigning it any value, as in: $EventObject = "". (Killing the event is optional.)

When you know the names of the events $oIE fires, you can code event-andling functions for them: name them per IEEvent_Eventname(parameters). Be sure to use the correct number of parameters, in the correct order, as specified for that particular event function; otherwise you may end up with unexpected results!

You don't have to implement all event-handling functions for an object. Those events for which there is no event-handling function will just be ignored.

More script examples using COM Event functions can be found at: http://www.autoitscript.com/autoit3/files/beta/autoit/COM/

Another Limitation of COM Events in AutoIt

Some objects (such as WebBrowser) pass arguments to their event-handling functions by reference. This is intended to allow the user change the values of parmeters, and pass the changes back to the object; however, AutoIt uses its own variable scheme, which is not fully compatible with COM variables. This means that all values from objects need to be converted into AutoIt variables, thus losing the reference to the original memory location. Perhaps in the near future we can remove 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 should you install an error-handling function in which you take action after the error has happened. This is not a solution for making 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-hnadling function. The only difference is the usage of the fixed string "AutoIt.Error" as the name of the object.

An example:

#include <MsgBoxConstants.au3>

Global $iEventError = 0 ; to be checked to know if COM error occurs. Must be reset after handling.

Local $oMyError = ObjEvent("AutoIt.Error", "ErrFunc") ; Install a custom error handler

; Perform a deliberate failure (the object doesn't exist)
Local $oIE = ObjCreate("InternetExplorer.Application")
$oIE.visible = 1
$oIE.bogus
If $iEventError Then
    MsgBox($MB_OK, "", "There was an error on the previous line.")
    $iEventError = 0 ; Reset after displaying a COM Error occurred
EndIf
Exit

; This is a custom error handler
Func ErrFunc()
    $sHexNumber = Hex($oMyError.number, 8)
    MsgBox($MB_OK, "", "We intercepted a COM Error !" & @CRLF & _
            "Number is: " & $sHexNumber & @CRLF & _
            "WinDescription is: " & $oMyError.windescription)
    $iEventError = 1 ; Use to check when a COM Error occurs
EndFunc   ;==>ErrFunc

An error handler is a special case of an event-handler, not only in the name of the object, but also:

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:

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 grayed 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 for interconnecting just about everythingthrough (their) software. It is used mainly for Web-based services.

COMmunist = This is not a supporter of COM, but someone who believes in communism (a theory that the common people should own all the property).