Perl の OLE 拡張モジュールのソース, Nifty-Serveのフォーラム,Windows プログラミング関連の書籍を調べて, OLE の IDispatchインターフェースと, IDispatchインターフェースがもつInvoke関数 が鍵だということがわかってきました.IDispatch 変数を内部にもつ Ruby のオブジェクトを作って そのオブジェクトに Invoke を起動するメソッドを定義しておけば 何とかなりそうだと見当をつけました.
拡張モジュールの名前は,「win32ole 拡張モジュール」に決めました. 別に「OLE拡張モジュール」でも良いかなとも考えましたが, Win32環境だけをターゲットとするという意味でwin32を つけておくことにしました. Win32API 拡張モジュールに合せておいた方が良いかとも考えたためです.クラス名も拡張モジュールの名前に合せて"WIN32OLE"と 決めました. さほど深くも考えずに, WIN32OLE は Object のサブクラスにしておくことにしました.
IDispatch 変数(Cの変数)を Ruby のオブジェクトとして扱うために, IDispatch をメンバーにもつCの構造体を定義します.この構造体を Ruby のDataオブジェクトと結び付けてやれば, Ruby のオブジェクトのできあがりです. (結び付けるためのCのマクロが Ruby では用意されています. Data_Make_Structと Data_Wrap_Struct です.)struct oledata { IDispatch *pDispatch; };実は,IDispatch へのポインタ変数1つだけであれば, わざわざ構造体にする必要はありません. そのまま IDispatch へのポインタ変数と Ruby の Dataオブジェクトを 結び付ければ良いのです. ですが,今後の拡張のことを考えて,構造体にしておくことにしました. (実際,参考にした Perlのソースコードでは,IDispatch 以外の変数も扱っています.)
IDispatch インターフェースを取得して何かして終了する流れは, 大雑把に以下のようになります.この流れをCのコードで書けばこんな感じです.
- OLE を使用するために OleInitialize 関数を実行して初期化する.
- CLSIDFromProgID関数を実行して 指定されたOLEオートメーションサーバー名の文字列からCLSIDを取得する.
- CoCreateInstance 関数を使って CLSIDからIDispatchインターフェースを取得する.
- 何かやりたいことをやる.
- OleUninitialize を実行して OLE の後始末する.
(エラーチェック等を省略してますのでそのまま使わないように.)CLSID clsid; IDispatch *pDispatch; /* OLE の初期化 */ OleInitialize(NULL); /* OLEオートメーションサーバー名(ProgID)から CLSID を取得 */ CLSIDFromProgID(L"Excel.Application", &clsid); /* CLSID から IDispatch インターフェースを取得する */ CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void**)&pDispatch); /* 何かやりたいことをする */ ・・・ /* OLE の後始末する */ OleUninitialize();
まず考えた,win32oleの仕様の(かなりC言語よりの)イメージはこんな感じでした.require "win32ole" ole = WIN32OLE.new #OLE の初期化 ole.initialize #Excel.Application の IDispatchインターフェースの取得 disp = ole.get_dispatch("Excel.Application") #何かする ・・・ #OLE の後始末 ole.uninitialize初期化と後始末を意識しなくてはならないのは, Rubyらしくなくて格好悪いです. こんな感じの方がスマートです.
そこでrequire "win32ole" #初期化は意識しない disp = WIN32OLE.new("Excel.Application") #何かする ・・・ #後始末は意識しないとしました.
- OLEの初期化とIDispatchインターフェースの取得は,new メソッドで行う.
- newメソッドは,OLEオートメーションサーバー名(ProgID)1つを引数として取る.
- DCOM(Distributed Component Object Model)には対応しない.
Perl の方は,DCOMも考慮していますが, Ruby の win32ole 拡張モジュールでは DCOMには対応しないことにしました.
excel1.pl,excel2.pl と同等のスクリプトの実現のために DCOMは必要ないためです.(基本方針1)- newメソッドは IDispatchインターフェースをくるんだRuby のオブジェクトを返す.
Rubyのオブジェクトを作成するには, Ruby で用意しているCのマクロ Data_Make_Struct を利用する.- OLEの後始末は,Rubyで用意しているCの関数 atexit を利用する.
(初期化と後始末の構造は,Ruby の socket 拡張モジュールを参考にしています.)