Excel の VBA で Workbook オブジェクトにアクセスするにはなどとします.book = Application.Workbooks("book1")これを,IDispatch インターフェースと Invoke関数を使って説明すると 以下のようなイメージになります.(これは私なりの解釈です.)
- "Excel.Application" の IDispatch インターフェースを取得する.
「Application.Workbooks("book1")」 というコードの 「Application」の部分に相当します.- 取得した IDispatch インターフェースの Invoke 関数を使って WorkBooks("book1")を実行する.
「Application.Workbooks("book1")」というコードの 「Workbooks("book1")」に相当します.擬似的な(C言語の)コードで書くとこんな感じです.
(あくまでも擬似コードです.コンパイルすると絶対エラーになります.)Ruby のスクリプトだとこんな感じになれば良さそうです./* IDispatch インターフェースを取得 */ CLSIDFromProgID(L"Excel.Application", &clsid); CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void**)&pDispatch); /* Invoke を使って Workbooks メソッドを実行 */ book = pDispatch->Invoke("Workbooks", "book1");# IDispatch インターフェースを取得 app = WIN32OLE.new("Excel.Application") # Workbooks メソッドを実行 book = app.invoke("Workbooks", "book1")invoke メソッドは,
としました.
- 第一引数としてOLEオートメーションオブジェクトのメソッドの名前を 文字列で受け取る.
- 第二引数以降は第一引数で指定されたメソッドの引数を受け取る.
従ってinvoke メソッドの引数の数は可変.- 第二引数以降の引数の型は,文字列以外も可とする.
- 戻り値は 第一引数で指定されたメソッドの実行結果を返す.
実際には,IDispatch インターフェースの Invoke 関数は 実行するメソッドの名前(文字列)をIDに変換した dispID を 受け取ってメソッドを起動します.
invokeメソッドの第一引数は,Ruby の文字列なのでという手順になります.
- 起動するメソッド名である Ruby の文字列を C の文字列に変換する.
RSTRING(str)->ptr とすれば,Ruby の文字列 str を C の文字列 ptr に変換できます.- 変換した C の文字列を dispID に変換する.
IDispatch インターフェースの GetIDsOfNames 関数を使えば変換できます.実行するメソッドの引数は,まとめて1つの DISPPARAMS構造体 にセットします. Invoke 関数にはこの DISPPARAMS 構造体を渡します. DISPPARAMS構造体は,次のように定義されています.
VARIANTARG 型は,実際のデータとそのデータ型を識別する情報が入っています. Ruby の VALUE型が,実際のデータとそのデータ型を識別する情報を持っている ことに対応します.typedef struct tagDISPPARAMS { VARIANTARG FAR* rgvarg; /* メソッドの引数の配列 */ DISPID FAR* rgdispidNamedArgs; /* 名前付き引数の dispID の配列 */ unsigned int cArgs; /* メソッドの引数の総数 */ unsigned int cNamedArgs; /* 名前付き引数の数 */ } DISPPARAMS;invoke メソッドの第二引数以降は,
- Ruby の引数(VALUE型)を C言語の対応する型の変数に変換する.
- C言語の型に変換した変数をVARIANTARG型に変換する.
(値とCの型情報をVARIANTARG型に格納する)- VARIANTARG型に変換した変数を rgvarg に格納する.
- cArgs に 1 を加算する.
という処理を繰り返します.
rgdispidNamedArgs には NULL を cNamedArgs には 0 を常に設定しておきます.名前付き引数は便利な機能なのですが,絶対必要な機能という訳ではないと 考えサポートしないことに決めました.(基本方針1)
Invoke は,Invokeの実行結果を格納するためのVARIANT型の変数へのポインタを 引数として持ちます. Invoke の実行結果は,このVARIANT型の変数の中身を見ることでわかります. 実は,VARIANT型は VARIANTARG型と全く同じ形式で定義されています. ですから,という手順で,Rubyの型に変換することができます.
- VARIANT型の変数を C言語の型に変換する.
- C言語の型を Ruby の変数の型(VALUE型)に変換する.
Excel の VBA では,プロパティの取得や設定といったものがあります.
Ruby では一応こんな感じにすれば良いかなと考えました.Dim book as Workbook Dim title as String book = Application.Workbooks("book1") # title プロパティの取得 title = book.title # title プロパティの設定 book.title = "MyBook"プロパティの取得は"[]"メソッド, プロパティの設定は"[]="メソッドを用意することで 実現できるようにすることにしました.app = WIN32OLE.new("Excel.Application") book = app.Workbooks("book1") # プロパティの取得 title = book['title'] # プロパティの設定 book['title'] = "MyBook"プロパティの取得も,プロパティの設定も IDispatch インターフェースの Invoke 関数で実現できます. プロパティの名前をメソッド名の代わりに使ってInvoke を呼び出します.
Invoke 関数は,どうやってプロパティの取得,プロパティの設定,メソッド の実行を区別するのでしょうか? 実は,Invoke 関数は引数として,Invoke が実行する処理のタイプを受け取ります. この引数を見てInvoke 関数は処理を区別することができます.
- メソッドの実行では DISPATCH_METHOD
- プロパティの取得には DISPATCH_PROPERTYGET
- プロパティの設定には DISPATCH_PROPERTYPUT
をそれぞれ引数に指定して,Invoke関数を呼び出せば良いのです.
Invoke関数は,メソッドの実行,プロパティの取得,プロパティの設定 のすべてをまかなっているスーパーマンなのです.