簡単なイントロダクション
COMとは"Component Object Model"の略です。これは共通のインターフェイスを使用してソフトウェア同士を相互に連結するMicrosoftの方法です。これらのインターフェイスはCOMオブジェクト内で定義されています。
COM登場以前はプログラムに'インターフェイス'するにはプログラムの正確な実装を知っている必要がありました。COMを使用するとCOMが定義されたオブジェクトを"対話的"に操作することができます。知っていなければならないことは使用するオブジェクトの名前とそれらが持つ'プロパティ'、'メソッド'だけです。
オブジェクトには2つの基本的性質があります。'プロパティ'はオブジェクトのデータ貯蔵庫といえます。また、'メソッド'はデータに何らかの処理をおこなうために呼び出す内部関数といえるでしょう。
状況によります。AutoItには多くのビルトイン関数とUDFの巨大なライブラリがあります。ほとんどのプログラミングはそれらを使っておこなうことができます。ただしあなたが他のアプリケーションと特別な'連携'をおこなう必要がある場合、COMの使用はスクリプトを書く助けになるでしょう。スクリプトを書く場合に注意が必要なのはCOMオブジェクトの存在はオペレーティングシステムとそこにインストールされているソフトウェアに大きく依存するということです。下のサンプルは全てMicrosoftOffice2000がインストールされた'通常の'WindowsXPプロフェッショナル下でテストされています。
開いているウィンドウを全て最小化したいとしましょう。これはWinList、WinSetStateといったAutoIt標準関数を使用しても処理できますが、2行のCOMコードでも同じことができます。
$oShell = ObjCreate("shell.application")
$oShell.MinimizeAll
サイドノート: AutoItにWinMinimizeAll()関数ができたので、この例は全てのウィンドウを最小化する最短の手段ではなくなりました。
1行目で"shell.application"と呼ばれる新しいオブジェクトを作成しています。これはshell32.dllで定義されているWindowsの内部オブジェクトです。このあたらしいオブジェクトへのポインタが変数$oShellに代入されます。これによって$oShellはオブジェクト変数になります。
2行目でoShellオブジェクトの"MinimizeAll"というメソッドを使用しています。これによって全てのウィンドウが最小化されます。
全てのバージョンのWindowsはさまざまな目的のために大量の内部オブジェクトを持っています。またExcelやWordといったアプリケーションも独自のオブジェクトのセットを持っています。
しかしながらシステムで定義されている全てのオブジェクトのプロパティとメソッドが載っているリストを入手することが困難な場合があります。Microsoft.comやGoogle.comの検索が使用したいオブジェクト'X'についての手がかりを与えてくれるでしょう。
例えば、"shell.application"オブジェクトについての情報はここで見つかります。
システムに現在インストールされている全てのオブジェクトを調べるには"OLE/COM Object Viewer"というツールがとても便利です。このツールは下の別セクションで説明します。
別のサンプルを見てみましょう。あるウェブページの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-Framenのトップ)の完全なHTMLコードが保持されます。
("winhttp.winhttprequest"オブジェクトについての情報はこちら:
http://msdn.microsoft.com/en-us/library/aa384106.aspx
)
次のことを忘れないでください:オブジェクトが存在するかどうかはコンピュータのオペレーティングシステムとそこにインストールされているプログラムに依存します。例えばwinhttp.winhttprequest.5.1オブジェクトはInternet Explorer version 5以降がインストールされているコンピュータのみに存在します。COMオブジェクトを使用しているスクリプトを共有する場合は全てのコンピュータにオブジェクトが存在することを確認してください。
オブジェクト変数の振る舞いは他のAutoIt変数と少し異なります。オブジェクトは本当の変数ではなくスクリプト外部への'ポインタ'です。従って算術演算や方程式に使用することはできません。オブジェクト変数にちがう値を代入すると'ポインタ'は自動的に解放されます。例えば数値、テキストを代入することでオブジェクトを強制削除することができます。
$oHTTP = ObjCreate("winhttp.winhttprequest.5.1") ; オブジェクト作成
$oHTTP=0
; オブジェクト削除
終了前にオブジェクトを削除する必要はありません。スクリプトが存在する場合、AutoItはスクリプト内で作成されたオブジェクトへのアクティブな参照の全てを解放しようとします。同じことは関数内でローカルなオブジェクト変数を定義し、関数をreturnで終了した場合もおこなわれます。
非常によく使われるCOMアプリケーションは'自動化'プログラムです。Send()やWinActivate()といった標準のAutoIt関数を使用する代わりに、プログラム内で定義されたオブジェクトを使用することができます。
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では2つの特別な文法がCOMのために用意されています。
WITH/ENDWITHとFOR/IN/NEXTループです。
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文は配列型変数にも使用できます)。
下に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 Arraytest","Result: " & @CRLF & $String)
ほとんどの場合、コレクションの要素を取得するのに'通常の'オブジェクトのメソッドを使用することはできません。'COM'の語法にのっとって言うとそれらを'列挙(enumerate)'する必要があります。その際にFOR..INループが必要になります。
下のExcelサンプルでは現在アクティブなシート上のセルA1:O16でループしています。もしセルの1つに5よりも小さい値がはいっていた場合、コードはその値を0(ゼロ)に置き換えます。
$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 ; サンプルデータでセルを埋める
numbers
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終了
以下にあるAutoItCOMの特徴の説明はCOMイベントとCOMエラーのハンドリングに関する広い知識を必要とします。
もしあなたがCOMプログラミング初心者の場合はまずいくつか良いCOMのドキュメント読んでください。
COMのバイブルはKraig Brockschmidt (Microsoft Press)の"Inside OLE 2"という本です。
またインターネット上にもいくつかの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自動化は主に一方向通信を使用します。あなたはオブジェクトに何かプロパティやメソッドの結果を'尋ねます'。しかしCOMオブジェクトは場合によってはあなたのスクリプトに'尋ね返す'ことができます。
あなたがなにかCOM関連のアクションが起きるのを待つ必要がある場合、これは非常に便利です。
ループを書く代わりに、もしなにか興味のあることが起きたらオブジェクトがあなたのスクリプト内の特定のUDFを呼び出すようにオブジェクトに依頼しておくことができます。そうしておいて(ほとんど)同時にスクリプト内で別の処理をおこうことができます。
すべてのオブジェクトがイベントをサポートしているわけではありません。イベントをサポートしているのかどうか、オブジェクトのドキュメンテーションを注意深く読む必要があります。
もしサポートしていた場合、次にすることはそれがサポートしているイベントの型を知ることです。AutoItCOMは'dispatch'型イベントのみ受け取ることができます。
最後にそのオブジェクトの生成できるイベントの名前と(もしあれば)そのための命令名を知る必要があります。
全ての情報がそろった場合のみ、COMイベントを使用したAutoItスクリプトの作成を開始できます。
下にInternet Explorerからイベントを受け取るデモの一部を示します。
$oIE=ObjCreate("InternetExplorer.Application.1") ;
Internet Explorerオブジェクト作成
$EventObject=ObjEvent($oIE,"IEEvent_","DWebBrowserEvents") ; イベント受け取り開始
$oIE.url= "http://www.autoitscript.com" ; サンプル用ウェブページのロード
;この瞬間から$oIEオブジェクトはウェブページロード中にイベントを発生するようになる
;それらは下記のイベント処理関数でハンドルされる
;ここでユーザーが終了するまでスクリプトを停止させることができます
...(ここにあなたのコード)...
$EventObject.stop
; IEにイベント受け取りを停止するように通知
$EventObject=0
; イベントオブジェクトの削除
$oIE.quit
; IE終了
$oIE=0
; メモリからIEを削除(なくてもよい)
Exit
; メインスクリプト終了
; いくつかのInternet Explorerイベント関数
;
; 全てのIEイベント関数のリストを見るにはMSDN WebBrowserドキュメンテーションを参照してください
; http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.aspx
Func IEEvent_StatusTextChange($Text)
; 完全なスクリプト(下記リンク参照)ではGUIエディットボックスに内容を表示している
GUICtrlSetData ( $GUIEdit, "IE Status text changed to: "
& $Text & @CRLF, "append" )
EndFunc
Func IEEvent_BeforeNavigate($URL, $Flags, $TargetFrameName, $PostData,
$Headers, $Cancel)
; 完全なスクリプト(下記リンク参照)ではGUIエディットボックスに内容を表示している
; ノート: 宣言の仕方がMSDNのものとは異なる
GUICtrlSetData ( $GUIEdit, "BeforeNavigate: " & $URL & " Flags:
" & $Flags & @CRLF, "append")
EndFunc
完全なスクリプトを見るにはここをクリック
このスクリプトの主要な部分は$EventObject=ObjEvent($oIE,"IEEvent_",...)です。
この関数はオブジェクト$oIEを取得し、そのイベントをMYEvent_で始まるAutoIt関数にリルートします。3番目のパラメータはオプションです。オブジェクトが複数のイベントインターフェイスを持っていて、それをAutoItに自動選択させたくない場合に使用します。
オブジェクトの動作の継続的なリルートは$EventObjectがおこないます。イベントを停止するとき以外はこの変数には特に注意をはらう必要はありません。
イベントのリルートを停止するには$EventObject=""のように変数を削除するだけではいけません。なぜなら'呼び出し'オブジェクトはまだこの変数への参照を保持していて、オブジェクト自体が終了するまで失わないからです。'呼び出し'オブジェクトを削除することでこの問題は解決できますが、 $EventObject.Stopを使用してオブジェクトにイベント受け取り停止を通知することもできます。その後、$EventObject=""のようにして値の代入でイベントを削除することができます(本当は必要ないのですが).
$oIEが起動するイベントの名前を知っている場合、 IE Event_Eventname(オプション引数)の名前をを持つAutoIt UDFを作成する代わりにイベントを実装することができます。イベント関数に正しい引数の数と順番を指定していることを確認してください。もし間違っていた場合、予期しない結果に終わります。
(なんらかの理由で)イベントの名前がわからない場合、接頭辞のみを持つUDFの追加が可能です。このサンプルの場合Func IEEvent_($Eventname)となります。
イベント受け取り時に
IEEvent_イベント名
のUDFが存在しないとこの関数が代わりに呼び出され、イベントの名前が変数$Eventnameに代入されます。
全てのイベント関数を実装する必要はありません。実装されていないものは単に無視されます。
COMイベント関数を使用したスクリプトのサンプルはhttp://www.autoitscript.com/autoit3/files/beta/autoit/COM/>からダウンロードできるAutoIt 3.1.1.xx beta ZIP配布ファイルのtestsディレクトリにあります。
AutoItでのCOMイベントの制限事項
オブジェクトのいくつか(例えば'WebBrowser')はそのイベント関数に引数を'参照渡し'します。これはユーザーが引数を変更してオブジェクトに通知できるようにと意図されたものです。しかしAutoIt自体の変数スキームはCOM変数と互換性がありません。これはオブジェクトから渡される全ての変数はAutoIt変数に変換される必要があり、従ってオリジナルのメモリスペースへの参照は失われるということです。おそらく近い将来われわれ開発チームはこの制限を解決できるでしょう!
適切なエラーハンドリングなしでのCOMの使用はとても厄介な事態を生みます。特にあなたがスクリプトでのオブジェクトに馴れていない場合はそうです。
AutoItスクリプトはCOMエラーを検知すると直ちに実行処理を停止します。これはデフォルトの設定であり、また最も安全な設定でもあります。この場合、あなたはエラーが起きないようにスクリプト内で措置を講じなければなりません。
COMエラーを防ぐ方法がない場合のみ、エラーが起きた後に処理をおこなう"エラーハンドリング"を導入することができます。これはバグだらけのスクリプトを正常に動かすための解決策ではありません。またCOMに関連していないスクリプトエラー(例えば宣言や文法上のエラー)を捕捉することもできません。
エラーハンドリングは通常のCOMイベントと同じ方法で実装します。ObjEvent()とユーザー定義のCOMイベント関数を使用するのです。違いは定数文字列"AutoIt.Error"をオブジェクトの名前に使用することだけです。
サンプル:
Global $g_eventerror = 0 ; エラーが起きていないかどうかのチェック用。ハンドリング後はリセットする必要がある。
$oMyError = ObjEvent("AutoIt.Error","MyErrFunc") ; 自作のエラーハンドラを読み込む
; ここでは意図的に失敗を実行する (オブジェクトが存在しない)
$oIE = ObjCreate("InternetExplorer.Application")
$oIE.visible = 1
$oIE.bogus
if $g_eventerror then Msgbox(0,"","the previous line got an error.")
Exit
; 独自のカスタムエラーハンドラー
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 ; この関数が返ったときのチェック用
Endfunc
エラーイベントハンドラーで特別なことの1つはオブジェクトをリターンするということです。これは利用可能なプロパティとメソッドを持っているAutoItエラーオブジェクトです。この実装の一部はVB(スクリプト)の"Err"オブジェクトに基づいています。
AutoItエラーオブジェクトのプロパティ:
.number | COM呼び出しのWindows HRESULT値 |
.windescription | .numberから派生したFormatWinError()テキスト |
.source | エラーを発生したオブジェクトの名前 (ExcepInfo.sourceの内容) |
.description | ソースオブジェクトのエラーの説明 (ExcepInfo.descriptionの内容) |
.helpfile | ソースオブジェクトのエラーのヘルプファイル (ExcepInfo.helpfileの内容) |
.helpcontext | ソースオブジェクトのヘルプファイルのコンテキストID番号 (ExcepInfo.helpcontextの内容) |
.lastdllerror | GetLastError()の返す数値 |
.scriptline | エラーが発生したスクリプト行 |
AutoItエラーオブジェクトのメソッド:
.raise | ユーザーによって初期化されているエラーイベントを発生させます。 |
.clear | エラーオブジェクトの内容を消去します(例えば 数値を0、文字列を"") |
UDF作成者のためのノート
1つのAutoItスクリプトにつき1つだけアクティブなエラーイベントハンドラーを持つことができます。COM関数を使っているUDFを書いている場合、次のようにしてユーザーがエラーハンドラーを導入しているかどうかを調べることができます。
$sFuncName = ObjEvent("AutoIt.Error")
if $sFuncName <> "" then Msgbox (0,"Test","User has installed Error
Handler function: " & $sFuncName)
アクティブなエラーハンドラーが存在しない場合、UDF呼び出し中に一時的に独自のエラーハンドラーを導入できます。
ただし、それが代入された変数を解放することなしに既存のエラーハンドラーを停止することはできません。スクリプト作成者がCOMエラーハンドラーを導入した場合、UDFで発生したCOMエラーもキャッチできるような適切な関数を使用するのは彼の責任です
"OLE/COM Object Viewer"はシステムにインストールされている全てのCOMオブジェクトの情報を取得するためのとても便利なツールです。これはWindows 2000リソースキットの一部で、 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というファイルがない」というメッセージが出ます。このファイルは不可欠ですが奇妙なことに最新セットアップに含まれていません。このdllは http://download.microsoft.com/download/2/f/1/2f15a59b-6cd7-467b-8ff2-f162c3932235/ovi386.exeにある古いバージョンのoleview.exeから取得することができます。デフォルトではファイルはC:\MSTOOLS\BINディレクトリにインストールされます。必要なのはiviewer.dllだけです。oleview.exeと同じディレクトリにコピーした後、コマンドライン:regsvr32 iviewers.dllを使用してdllを登録してください。
Oleviewerの使用例を見てみましょう。実行して Object Classes->Grouped by Component Category->Control->Microsoft Web Browserというツリーを見てください。
左の囲みに このオブジェクトで定義されている全てのCOMインターフェイス が表示されます。これらについては後で説明します。右の囲みを見てみましょう。ここにはこのオブジェクトをAutoItスクリプトで使用するためのたくさん情報があります。1番重要なのは"VersionIndependentProgID"です。これはObjCreate、ObjGet、ObjEvent関数で使用する名前です。さらにオブジェクトのあるディレクトリとファイル名があります。これらはEXE、DLL、OCXファイルのいずれかです。InProcServer32はそのオブジェクトが(プロセス内で)あなたのスクリプトと同じスレッドで実行されることを表しています。LocalServer32の場合はオブジェクトは別のプロセスで実行されます。オブジェクトにはタイプライブラリ("TypeLib="に続く行)が含まれていなければなりません。ない場合、AutoItスクリプト内で使用することはできません。
左の囲みのインターフェイスはオブジェクトと通信するために使用されます。情報保持に使用されるもの(IStorage, IPersist)とGUIに埋め込んで使用するもの(IOleObject, IOleControl)があります。AutoItは自動化のためにIDispatchインターフェイスを使用します。このインターフェイスはオブジェクトがサポートする全てのスクリプトに使用できるメソッドとプロパティを'公開'しています。
このインターフェイスを見てみましょう。 IDispatchを右クリックしてコンテキストメニューから"View..."を選びます。その後、"View TypeInfo..."ボタンをクリックします(注:このボタンがグレイアウトしている場合、iviewers.dllファイルが登録されていないかオブジェクトがタイプライブラリを持っていません)。
"ITypeInfo Viewer"ウィンドウはオブジェクトが提供する情報を表示するだけです。開発者がヘルプファイルを添付しないと決定した場合、メソッド/プロパティの名前が確認できるだけで他には何もわかりません。しかし"Microsoft Web Browser"タイプライブラリは非常に情報が多いです。左の囲みのなかのアイテムをクリックすると、説明が右の囲みに表示されます。オブジェクトのメソッドをより多く得るために"継承されたインターフェイス(Inherited Interfaces)"を参照する必要がある時もあります。
メソッド/プロパティの説明に使用される構文はC/C++スタイルです。説明で"HRESULT Resizable([in] VARIANT_BOOL pbOffline)"とあるプロパティは $Resizable=$Object.Resizable ($ObjectはObjCreateかObjGetで作成されたオブジェクトを保持している) というようにAutoIt風に書き直す必要があります。
これらの用語は一般にCOMと混同されていますが異なる意味を持っています。
OOP = Object Oriented Programming(オブジェクト指向プログラミング)。オブジェクトという再利用可能な作成済みブロックを組み合わせてソフトウェアコンポーネントを作成するプログラミング技法。
DDE = Dynamic Data Exchange.
COMの前身といえます。異なるアプリケーション間の情報とコマンドの転送にIPCを使用しています。
OLE = Object Linking and Embedding
最初のバージョンではOLEはあるプログラムから別のプログラムにデータを'埋め込む'ためのDDEの拡張バージョンでした。現在のOLEはCOM上に構築され、ActiveXの一部です。
Automation = 他アプリケーションのオブジェクトを操作する方法です。OLE、ActiveX、COMで使用されています。
ActiveX = Automationを使用した次世代のOLEです。初期は主にネットワークを介したアプリケーション(特にウェブブラウザ)間のインターフェイスとして発達しました。ActiveXはCOM上に構築されています。
DCOM=Distributed COM 異なる物理コンピュータ間でも通信ができるようにCOMを微修正したものです。
.NET (dot Net) = これは実際はソフトウェアの一部ではなく、Microsoftが発案した(彼らの)ソフトウェアを通じて'全て'が相互に連結するという'アイデア'です。"dot Net"は主にウェブベースのサービスで使用されます。
COMmunist = COMのサポーター、ではなくコミュニズム(庶民がすべての財産を所有するべきだとする理論)を信望している人のことです。