AutoIt COM 扩展

一个简短的介绍

什么是 COM?

COM 是 "Component Object Model"(组件对象模型) 的缩写. 是微软为应用程序相互连接使用 的一种公共接口. 这些接口定义为 COM 对象(Object).

在开始 COM 编程时, 您必须知道严格的程序是怎么执行的. 最先开始,您所了解的程序是一个"接口"(interface). 使用 COM, 您可以 "叫" 它为一个对象(Object).  您必须了解这些对象(Objects) 的名称("name").属性('properties')或者方法( 'methods').

 

什么是对象(Object)的属性或者方法?

这是对象(Object)的两个最基本的特征.  属性('property')里面保存的是对象所需的数据. 方法('method')是执行对象内部的一些函数执行一些事件.

我需要在 AutoIt 脚本里面使用COM吗?

这是绝对的. AutoIt 有很多内置的函数,也有巨大的 UDF 函数库.  您可以使用它们做出绝大多数的程序. 可是如果您需要指定一个接口('interfacing')连接到其它程序, 使用 COM 可以帮助您节省很多行的脚本代码.   编写脚本需要知道有哪些已经存在的 COM 对象是严重依赖操作系统和已经安装的软件的. 下方的所有例子都已经在纯净安装的 Windows XP 专业版上面通过测试(安装有 Microsoft Office 2000).

 

 

一个在 AutoIt 中使用COM的例子

 

假设您想知道怎么最小化所以打开的窗口. 您可能会使用正规的 AutoIt 函数,就像 WinList 和 WinSetState. 可是如果两行的 COM 代码就能得到同样的效果呢?: (不要告诉我用WIN+D)...

 

$oShell = ObjCreate("shell.application")

$oShell.MinimizeAll

小提示: 这个简短的代码只是让您知道怎么使用COM来最小化所有窗口, 虽然您可以使用 WinMinimizeAll() 函数来做.

 

在第一行,我们创建了一个名叫 "shell.application" 的对象. 这是一个 Windows 内部对象, 定义于 shell32.dll. 分配的变量 $oShell 指向这个新对象; $oShell 现在就是一个对象变量.

 

在第二行, 我们使用了一个名叫 "MinimizeAll" 的 oShell 对象方法.  它将最小化所有窗口.

 

所有 Windows 系统都有一个巨大的内部对象库为不同用途简化操作.   同样一些应用程序,就像Excel 或者Word 同样也有自己的对象库.  

可是,有时要获得一个您系统上面所有存在的对象的属性和方法 是非常困难的.   搜索 Microsoft.com 或者 Google.com 可以帮助您找到一些关于您想使用的 Object 'X' 的线索.

 

例如, 您可以在这里找到关于 "shell.application" 对象的一些信息:
http://msdn.microsoft.com/en-us/library/bb774094.aspx


要查看一下当前安装在您的计算机系统上面的所有对象,  "OLE/COM Object Viewer "(OLE/COM 对象查看器) 是一个相当有用的工具. 而且这个工具会有所选的项目的一些说明.

 

让我们来看一个其它例子.  我们想获得某一个 HTML 网页的源代码. 您可以使用 InetGet() 函数保存网页文件后再用 FileRead() 来读取内容. 但是这里只有三行代码就可以搞定:

 

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

 

字符串变量 $HTMLSource 现在包含了AutoItScript.com 主页完整的 HTML 源代码. (更确切的说,是顶层 HTML 框架).

 

(关于 "winhttp.winhttprequest" 对象的相关信息你可以在这里得到:
http://msdn.microsoft.com/en-us/library/aa384106.aspx )

 

请注意这里: 一些对象依赖于计算机的操作系统或者已经安装的程序. 例如: winhttp.winhttprequest.5.1 对象只存在于安装有 Internet Explorer 5.0 及更高的操作系统.  当您要共享您使用 COM 对象的脚本时, 请先确认对象是否存在于所有计算机.

 

对象变量和其它类型的 AutoIt 变量略有不同. 一个对象不是一个真实值, 而是指向('pointer')脚本以外的一些东西. 因此你不能对它进行数学运算, 也不能等于一个对象变量. 当你分配一个对象变量一个不同的值, 指向('pointer')也会自动释放.  你可以看下面的例子, 强制定义一个对象变量为任何数字或者文本值.

thesnoW:指向('pointer')姑且可以译作"指针",但要注意,真正意义上的指针是指向一个内存地址,如C/C++

 

$oHTTP = ObjCreate("winhttp.winhttprequest.5.1")  ; 对象被 创建
$oHTTP=0                                          ; 对象被删除

 

当一个脚本执行完成,您 不需要 删除对象变量. 如果脚本退出, AutoIt 会尝试释放脚本创建的所有活动对象. 对象会产生一些事件,您可以定义一个局部对象变量在函数CALL里面, 并在函数结束时返回结果.

 

使用 COM 进行自动化操作

 

一个相当流行的COM操作是"自动化"操作程序.  代替正规的Autoit操作函数,如:Send() 或者 WinActivate(), 您可以使用对象来制作一个调用对象内部函数的程序.

 

这里有一个例子,来'自动化'操作 Microsoft Excel:

 

$oExcel = ObjCreate("Excel.Application")                   ; 创建一个Excel 对象
$oExcel.Visible = 1                                        ; 显示 Excel 自己
$oExcel.WorkBooks.Add                                      ; 添加一个新的工作表
$oExcel.ActiveWorkBook.ActiveSheet.Cells(1,1).Value="test" ; 填写一个表格
sleep(4000)                                                ; 等待 4 秒
$oExcel.ActiveWorkBook.Saved = 1                           ; 模拟用户保存工作表
$oExcel.Quit                                               ; 退出 Excel 

 

控制其它程序的方法是复杂的, 不只是使用 AutoIt 脚本.  如果一些自动化(使用对象的)程序不能工作, 应该参考那个应用程序的文档,而不是AutoIt的帮助文件.


 

特殊语句

 

在 AutoIt 中, 两个特殊的语句来定义和使用 COM 对象:

WITH/ENDWITH 和 FOR/IN/NEXT loop.

 

 

WITH..ENDWITH

 

WITH/ENDWITH 语句不能添加功能, 但是它可以让你的脚本 更易阅读. 下面这个例子使用 Excel 同样可以这样 写:

 

$oExcel = ObjCreate("Excel.Application")                ; 创建一个 Excel 对象

WITH $oExcel
    .Visible = 1                                        ; 显示 Excel 自己
    .WorkBooks.Add                                      ; 添加 新工作表
    .ActiveWorkBook.ActiveSheet.Cells(1,1).Value="test" ; 填写一个表格
    sleep(4000)                                         ; 等待 4 秒
    .ActiveWorkBook.Saved = 1                           ; 模拟保存工作表
    .Quit                                               ; 退出 Excel
ENDWITH

这个例子能帮助你节省不少代码, 几乎所有的需要很长一段代码的属性/方法 都可以用WITH来缩写.

 

 

FOR..IN

 

FOR...IN 循环必须在使用 Collections 时使用.  Collections 是一个 指定的对象类型, 要是存在多个子对象. 您可以 把它们看成一个数组 (事实上,FOR..IN 也同样工作于数组类型(Array-type)变量).

 

FOR..IN 循环使用数组

下面这个例子演示了 FOR..IN 循环.  这个例子使用正常的AutoIt数组. 因此,它与 COM 无关. 只是给你说说大概原理:

 

$String = ""               ; 空字符串变量

$aArray[0]="a"             ; 我们填写一个数组
$aArray[1]=0               ; 使用几个不同的值
$aArray[2]=1.3434          ; 来填写它们.
$aArray[3]= "testestestest" ; 

FOR $Element IN $aArray    ; 这里为开始处..
   $String = $String & $Element & @CRLF
NEXT

; 显示结果给用户
Msgbox(0,"For..IN 数组测试","结果: " & @CRLF & $String)

 

 

FOR..IN 循环使用一个对象

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):

 

$oExcel = ObjCreate("Excel.Application") ; 创建一个 Excel 对象

$oExcel.Visible = 1               ; 显示 Excel 自己
$oExcel.WorkBooks.Add             ; 添加一个新的工作表

dim $arr[16][16]                  ; 这几行
for $i = 0 to 15                  ; 会给一些
  for $j =  0 to 15               ; 表格
   $arr[$i][$j] = $i              ; 填写
  next                            ; 内容.
next

$oExcel.activesheet.range("A1:O16").value = $arr ; 使用上面的数字来 填写表格内容

sleep(2000)                       ; 等待两秒

For $cell in $oExcel.ActiveSheet.Range("A1:O16")
   If $cell.Value < 5 Then
       $cell.Value = 0
   Endif
Next

$oExcel.ActiveWorkBook.Saved = 1 ; 模拟用户保存工作表
sleep(2000)                      ; 等待两秒
$oExcel.Quit                     ; 退出Excel

 


 

高级的 COM 使用

 

下面这些是使用 AutoItCOM 必须了解的知识( COM 事件和 COM错误句柄).

如果你是一个 COM 程序员新手, 请先阅读一些关于 COM 的好文章.

 

有个关于 COM 的"圣经"叫做 "Inside OLE 2" ,Kraig Brockschmidt 编写的(Microsoft Press).

 

您可以在网上找到更多的关于 COM 的资源 (不是 AutoIt 有关系的):

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

http://www.garybeene.com/vb/tut-obj.htm (关于 Visual Basic 中的对象)

http://java.sun.com/docs/books/tutorial/java/concepts/     (在Java中使用对象)

http://msdn.microsoft.com/archive/en-us/dnarguion/html/drgui082399.asp (在 C++中 使用对象事件)

http://www.garybeene.com/vb/tut-err.htm (Visual Basic 中的错误句柄)

 

 

COM 事件

 

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 cases 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:

 

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

$EventObject=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)... 

$EventObject.stop            ; Tell IE we want to stop receiving Events
$EventObject=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/system.windows.forms.webbrowser.aspx

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

Func IEEvent_BeforeNavigate($URL, $Flags, $TargetFrameName, $PostData, $Headers, $Cancel)
; 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: " & $URL & " Flags: " & $Flags & @CRLF, "append")
EndFunc

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 MYEvent_.  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 IE Event_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. 

 

If you don't know (for some reason) the names of the events, you can add a UDF with only the prefix. In this example: Func IEEvent_($Eventname).
When an event is received and no IEEvent_ Eventname UDF exists, this function will be called instead and the name of the event will be placed in the variable $Eventname.

 

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 in the tests directory in the AutoIt 3.1.1.xx beta ZIP distribution file, downloadable from: http://www.autoitscript.com/autoit3/files/beta/autoit/COM/

 

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 it's 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 loosing the reference to the original memory space.  Maybe in the near future we can solve this limitation for you !

 

COM 错误句柄

 

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

 

An AutoIt script will immediately stop execution when it detects a COM error.  This is the default and also the safest setting.  In this case you have to take measures in your script to prevent the error from happening.

 

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:

 

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

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

; Performing a deliberate failure here (object does not exist)
$oIE = ObjCreate("InternetExplorer.Application")
$oIE.visible = 1
$oIE.bogus
if $g_eventerror then Msgbox(0,"","the previous line got an error.")

Exit


; This is my custom error handler
Func MyErrFunc()
   $HexNumber=hex($oMyError.number,8)
   Msgbox(0,"","We intercepted a COM Error !" & @CRLF & _
                "Number is: " & $HexNumber & @CRLF & _
                "Windescription is: " & $oMyError.windescription )

   $g_eventerror = 1 ; something to check for when this function returns
Endfunc

 

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 only have ONE Error Event Handler active per AutoIt script. If you are writing UDF's containing COM functions, you can check if the user has an Error Handler installed as follows:

$sFuncName = ObjEvent("AutoIt.Error")
if $sFuncName <> "" then Msgbox (0,"Test","User has installed Error Handler function: " & $sFuncName)

If no Error Handler was active, you can temporarily install your own during the UDF call.

 

However, you can never stop an existing Error Handler without releasing the variable it had been assigned to. If the script author had installed a COM Error Handler, it's his responsibility to use a proper function that will also be able to catch COM errors generated by UDF's.

 

OLE/COM Object Viewer(OLE/COM对象查看器)

 

"OLE/COM 对象查看器" 是一个非常有用的工具, 它可以得到当前系统上面的所有 COM 对象. 你可以在 Windows 2000 resource kit 找到或者在这里下载: http://www.microsoft.com/downloads/details.aspx?familyid=5233b70d-d9b2-4cb5-aeb6-45664be858b6&displaylang=en

安装这个程序相当郁闷. 它不会创建任何图标到开始菜单给你. 因此, 你要到下面的路径来执行一个叫 oleview.exe 的文件,通常它会被安装在这里: C:\Program Files\Resource Kit 文件夹 (默认安装).

当运行 oleview.exe,很多系统 会报告缺少一个叫 iviewers.dll 的文件. 这个文件是必须的,但是奇怪的没有包含在最新的安装程序里面. 你可以在这里得到 oleview.exe说使用的DLL文件: http://download.microsoft.com/download/2/f/1/2f15a59b-6cd7-467b-8ff2-f162c3932235/ovi386.exe.  它会默认安装文件到 C:\MSTOOLS\BIN 文件夹. 你只需要 iviewer.dll 文件. 复制它到oleview.exe所在的目录, 如果需要注册这个DLL文件,可以使用命令行的 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 column you see all 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 to use this object in an AutoIt script. Most important is the "VersionIndependentProgID". This is the name to be used in an ObjCreate, ObjGet or ObjEvent function. 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 column are used for several 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 it does not exist, 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 did not have registered the iviewers.dll file, or the object does not have a type library)

 

 

The "ITypeInfo Viewer" window does only show the information that is provided with the object. 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 column, 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 meaning: 

 

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 his 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.

 

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).