スクリプトによる拡張2
2009年4月9日公開
<< 前のページへ
前のページでは、自分で作ったプログラムでVBScriptを実行する方法を説明した。ここからは、VBScriptからC++のコードを呼び出す方法について説明する。
とりあえず、VBScriptから自分で作ったC++のコードを呼び出すことができるのであれば、後はC++の世界でアプリケーションを制御することができるはずである。

COMコンポーネントの作成
C++で作ったプログラムをVBScriptから呼び出して実行するためには、COMコンポーネントを作成して、それをMSScript.ocxに組み込んでやらなければならない。
だから、まずは呼び出される側のCOMコンポーネントを作成する。
モジュールのひな形の作成
まずはウィザードでCOMコンポーネントのひな形を作成する。



とりあえず、話を簡単にするためにATL COMのDLLを作成するものとする。
COMオブジェクトを追加する
モジュールのひな形を作ったら、COMのオブジェクトを追加する



難しいことをやっても仕方がないから、シンプルオブジェクトを選択しておく。また、オブジェクトの名前はTestObjとしておく。
メソッドを追加する
VBScriptのコードから呼び出されるメソッドを追加する。


メソッド名は"ShowMessage"としておく。引数としては、Msgという文字列を一つだけ受け取るようにしておく。
メソッドの処理は下記のようにしておく。すなわち、引数に与えられた文字列をメッセージボックスに表示するだけである。
STDMETHODIMP CTestObj::ShowMessage(BSTR Msg)
{
////////////////////////////////////////////////////
// 以下を追加
MessageBox( NULL, Msg, _T( "" ), MB_OK );
////////////////////////////////////////////////////
return S_OK;
}
|
コンパイル
とりあえず、Unicode Debugを選択してコンパイルする。Unicodeを選ぶのは、文字列の種類による面倒を省くためである。

インストール
COMコンポーネントはレジストリに登録しなければ使うことができない。また、呼び出し側のアプリケーションをコンパイルする際に参照できる必要があるため、PATHの通ったディレクトリに格納される必要がある。
とりあえずここでは、windowsのシステムディレクトリ(Windows 2000やNT 4.0ならc:\WINNT、XPならc:\Windows)に、作成したDLLを格納しておくことにする。

DLLを所定の位置に配置したら、regsvr32コマンドによりCOMコンポーネントをレジストリに登録する。

登録に成功すると下記のようなメッセージが表示される。

以上の作業により、呼び出される側のCOMコンポーネントが完成した。
呼び出し側アプリケーションの変更
呼び出されるCOMコンポーネントができたら、次は呼び出す側のアプリケーションを変更してやらなければならない。すなわち、上で作ったCOMコンポーネントのインスタンスを生成して、MSScript.ocxに組み込んでやる作業を行わなければならない。
まずはStdAfx.hに作成したSTObj.dllを使うための宣言を記述する。
#include <afxwin.h> // MFC のコアおよび標準コンポーネント
#include <afxext.h> // MFC の拡張部分
#include <afxdisp.h> // MFC のオートメーション クラス
#include <afxdtctl.h> // MFC の Internet Explorer 4 コモン ・・・
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC の Windows コモン コントロール サポート
#endif // _AFX_NO_AFXCMN_SUPPORT
/////////////////////////////////////////////////////////
// 以下を追加
#import "msscript.ocx"
using namespace MSScriptControl;
#import "STObj.dll"
using namespace STOBJLib;
/////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。
|
太字の部分が、今回新たに追加した記述である。
今まではMSScript.ocxだけを使っていたのだが、今回新たに自分で作ったSTObj.dllも使うようになったので、その宣言を追加している。
なお、名前空間の"STOBJLib"は、STObj.idlファイルのlibrary句で宣言された名前である。
次はOKボタンが押下されたときの処理である。
void CScrTestDlg::OnOK()
{
///////////////////////////////////////////////////////////////
// 以下を追加
// CDialog::OnOK();
CString ScriptStr;
_bstr_t ScriptBstr;
CEdit *pEdit = (CEdit*)GetDlgItem( IDC_EDIT1 );
// 入力されたスクリプトを取得する
pEdit->GetWindowText( ScriptStr );
ScriptBstr = ScriptStr;
try {
// MSScriptのインターフェースを取得する
IScriptControlPtr pSC( __uuidof( ScriptControl ) );
// 自分が作ったオブジェクトのインタフェースを取得する
ITestObjPtr pTO( __uuidof( TestObj ) );
// 言語としてVBScriptを使うことを設定する
pSC->PutLanguage( _bstr_t( "VBScript" ) );
// MYOBJという名前でオブジェクトを追加する
pSC->AddObject( _bstr_t( "MYOBJ" ), pTO, TRUE );
// 実行
pSC->AddCode( ScriptBstr );
}
catch ( _com_error &e ) {
// エラーが発生したので、メッセージを表示する
AfxMessageBox( e.ErrorMessage() );
}
//////////////////////////////////////////////////////////////
}
|
上記も同様、太字の部分が今回新たに追加された記述である。
tryブロックの2行目で、自分で作成したTestObjのインスタンスを作成して、そのインタフェースを取得している。
tryブロックの4行目、すなわち、ScriptControlに対して使用する言語を通知した後に、作成したTestObjのインタフェースをScriptControlに渡している。その際、VBScript上では、このTestObjのインスタンスを"MYOBJ"という名前で参照できるように指定している。
以上の変更で、VBScriptからC++のCOMコンポーネントを呼び出せるようになる。
実行
実行してみる。

組み込んだオブジェクトは、VBScript上ではMYOBJという名前で参照される。でもって、そのMYOBJには文字列を引数に取るShowMessageというメソッドが一つだけ定義されているはずである。
この状態でOKボタンを押下するとこうなる。

判りにくいが、一応ShowMessageメソッドに記述したMessageBox関数が実行されている。
説明
OKボタンを押下した瞬間に何が起きているのか、上記の実行例だけでは判らないため、概念的に説明する。
まず、OKボタンが押下されるとアプリケーションはMSScript.ocxが提供するScriptConrolオブジェクトのインスタンスを生成する。

次に、自分で作ったSTObj.dllが提供されるTestObjのインスタンスを生成する。

TestObjのインスタンスを作ったら、ScriptControlの初期化(すなわち使用する言語の指定)を行っている。
本来であれば、この辺でTestObjのインスタンスに対しても、諸々の初期化処理を行うべきではあるのだが、今回は特にそういったことは必要ないため何も行っていない。
次に、ScriptControlに対してTestObjのインタフェースを渡している。このとき、VBScriptから参照する際の名称も一緒に指定している。

その後、アプリケーションはエディットボックスから取得したVBScriptのプログラムをScriptControlのインスタンスに渡して、スクリプトを実行させている。その際、VBScriptが"MYOBJ"という名前を参照したら、その名前が指す値としてTestObjのインスタンスが使用される。

この例では、TestObjが呼び出し元のアプリケーションへの参照を持たないため、VBScriptからTestObjを呼び出してもアプリケーションに結果をフィードバックすることができない。しかし、TestObjのインスタンスを生成した時にアプリケーションのコードを呼び出せるように工夫してやるか、あるいは、VBScriptの処理が終了した時点でアプリケーションからTestObjのインスタンスを参照して値を取得する様にすることは可能であり、やりようはいくらでもある。
|