Ruby win32ole 拡張モジュールの覚え書き(3)

Win32OLE製作過程の目次へ

invoke メソッド

Excel の VBA で Workbook オブジェクトにアクセスするには
book = Application.Workbooks("book1")
などとします.

これを,IDispatch インターフェースと Invoke関数を使って説明すると 以下のようなイメージになります.(これは私なりの解釈です.)

  1. "Excel.Application" の IDispatch インターフェースを取得する.
    「Application.Workbooks("book1")」 というコードの 「Application」の部分に相当します.
  2. 取得した IDispatch インターフェースの Invoke 関数を使って WorkBooks("book1")を実行する.
    「Application.Workbooks("book1")」というコードの 「Workbooks("book1")」に相当します.

擬似的な(C言語の)コードで書くとこんな感じです.
(あくまでも擬似コードです.コンパイルすると絶対エラーになります.)

/* 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");
Ruby のスクリプトだとこんな感じになれば良さそうです.
# IDispatch インターフェースを取得
app = WIN32OLE.new("Excel.Application")

# Workbooks メソッドを実行
book = app.invoke("Workbooks", "book1")

invoke メソッドは,

としました.

invoke メソッドの引数

実際には,IDispatch インターフェースの Invoke 関数は 実行するメソッドの名前(文字列)をIDに変換した dispID を 受け取ってメソッドを起動します.
invokeメソッドの第一引数は,Ruby の文字列なので
  1. 起動するメソッド名である Ruby の文字列を C の文字列に変換する.
    RSTRING(str)->ptr とすれば,Ruby の文字列 str を C の文字列 ptr に変換できます.
  2. 変換した C の文字列を dispID に変換する.
    IDispatch インターフェースの GetIDsOfNames 関数を使えば変換できます.
という手順になります.

実行するメソッドの引数は,まとめて1つの DISPPARAMS構造体 にセットします. Invoke 関数にはこの DISPPARAMS 構造体を渡します. DISPPARAMS構造体は,次のように定義されています.

typedef struct tagDISPPARAMS {
    VARIANTARG FAR* rgvarg;             /* メソッドの引数の配列 */
    DISPID FAR*     rgdispidNamedArgs;  /* 名前付き引数の dispID の配列 */
    unsigned int    cArgs;              /* メソッドの引数の総数 */
    unsigned int    cNamedArgs;         /* 名前付き引数の数 */
} DISPPARAMS;
VARIANTARG 型は,実際のデータとそのデータ型を識別する情報が入っています. Ruby の VALUE型が,実際のデータとそのデータ型を識別する情報を持っている ことに対応します.

invoke メソッドの第二引数以降は,

  1. Ruby の引数(VALUE型)を C言語の対応する型の変数に変換する.
  2. C言語の型に変換した変数をVARIANTARG型に変換する.
    (値とCの型情報をVARIANTARG型に格納する)
  3. VARIANTARG型に変換した変数を rgvarg に格納する.
  4. cArgs に 1 を加算する.

という処理を繰り返します.
rgdispidNamedArgs には NULL を cNamedArgs には 0 を常に設定しておきます.

名前付き引数は便利な機能なのですが,絶対必要な機能という訳ではないと 考えサポートしないことに決めました.(基本方針1


invokeメソッドの戻り値

Invoke は,Invokeの実行結果を格納するためのVARIANT型の変数へのポインタを 引数として持ちます. Invoke の実行結果は,このVARIANT型の変数の中身を見ることでわかります. 実は,VARIANT型は VARIANTARG型と全く同じ形式で定義されています. ですから,
  1. VARIANT型の変数を C言語の型に変換する.
  2. C言語の型を Ruby の変数の型(VALUE型)に変換する.
という手順で,Rubyの型に変換することができます.

プロパティの取得と設定

Excel の VBA では,プロパティの取得や設定といったものがあります.
Dim book as Workbook
Dim title as String
book = Application.Workbooks("book1")

# title プロパティの取得
title = book.title

# title プロパティの設定
book.title = "MyBook"
Ruby では一応こんな感じにすれば良いかなと考えました.
app = WIN32OLE.new("Excel.Application")
book = app.Workbooks("book1")
# プロパティの取得
title = book['title']
# プロパティの設定
book['title'] = "MyBook"
プロパティの取得は"[]"メソッド, プロパティの設定は"[]="メソッドを用意することで 実現できるようにすることにしました.

プロパティの取得も,プロパティの設定も IDispatch インターフェースの Invoke 関数で実現できます. プロパティの名前をメソッド名の代わりに使ってInvoke を呼び出します.

Invoke 関数は,どうやってプロパティの取得,プロパティの設定,メソッド の実行を区別するのでしょうか? 実は,Invoke 関数は引数として,Invoke が実行する処理のタイプを受け取ります. この引数を見てInvoke 関数は処理を区別することができます.

をそれぞれ引数に指定して,Invoke関数を呼び出せば良いのです.

Invoke関数は,メソッドの実行,プロパティの取得,プロパティの設定 のすべてをまかなっているスーパーマンなのです.


Win32OLE製作過程の目次へ