JScriptからC++のクラスメソッドを呼び出す 2
MyScriptSiteクラスの実装
JScriptを実行するにはIActiveScriptSiteを継承したクラスを用意します。ここではMyScriptSiteクラスとします。クラス図を下図に示します。

まずIUnknownを実装します。QueryInterfaceにて、インターフェースIDにIID_IActiveScripteSiteが渡された場合、IActiveScriptSiteインターフェースへのポインタを返すようにします。ここでもSimpleMsgBoxクラスと同様、IMPL_IUNKNOWNマクロを使います。
class MyScriptSite : public IActiveScriptSite { IMPL_IUNKNOWN(IActiveScriptSite) IActiveScript *pAs; IActiveScriptParse *pAsp; public: MyScriptSite() : refCount(1), pAs(NULL), pAsp(NULL) { } virtual ~MyScriptSite() { }
次にIActiveScriptSiteの実装を行います。このインターフェースはスクリプトを実行する際に、スクリプトエンジンからコールバックされるメソッドが宣言されています。
OnScriptErrorはスクリプトの実行エラー発生時に呼び出されます。引数として渡されるIActiveScriptErrorのGetExceptionInfoを使うことにより、エラーメッセージの文字列を取得できます。ここでは、それをメッセージボックスに表示させています。
GetItemInfoはスクリプトで使用されるオブジェクトを作成します。作成したオブジェクトは引数のppunkItemに代入しています。
他のメソッドについては、特に実装は行いません。
// MyScriptSite クラスに追加 // IActiveScriptSite STDMETHODIMP GetLCID(LCID*pLcid) { return E_NOTIMPL; } STDMETHODIMP GetDocVersionString(BSTR *pbstrVersionString) { return E_NOTIMPL; } STDMETHODIMP OnScriptTerminate(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) { return S_OK; } STDMETHODIMP OnStateChange(SCRIPTSTATE ssScriptState) { return S_OK; } STDMETHODIMP OnScriptError(IActiveScriptError *pAse) { HRESULT hr; if(pAse==NULL) return E_POINTER; EXCEPINFO ei; hr=pAse->GetExceptionInfo(&ei); if(SUCCEEDED(hr)) MessageBox(NULL, ei.bstrDescription, ei.bstrSource, MB_OK | MB_ICONSTOP); return hr; } STDMETHODIMP OnEnterScript(void) { return S_OK; } STDMETHODIMP OnLeaveScript(void) { return S_OK; } STDMETHODIMP GetItemInfo(LPCOLESTR pstrName,DWORD dwReturnMask, IUnknown **ppunkItem,ITypeInfo **ppTypeInfo) { if((dwReturnMask & SCRIPTINFO_IUNKNOWN)) { if(wcscmp(pstrName,SimpleMsgBox::GetClassName())==0) { *ppunkItem=(IUnknown*)new SimpleMsgBox(); return S_OK; } } return TYPE_E_ELEMENTNOTFOUND; }
スクリプトエンジンでスクリプトを実行する
スクリプトの実行準備を行うPrepareメソッドをMyScriptSiteクラスに追加します。
スクリプトエンジンのインターフェースIActiveScriptを取得し、SetScriptSiteでMyScriptSiteクラスのポインタを指定します。次にIActiveScriptParseインターフェースを取得しInitNewを呼びます。最後にAddNamedItemを呼んでSimpleMsgBoxクラスをグローバルオブジェクトとして追加しています。
// MyScriptSite クラスに追加 void Prepare() { CLSID clsid; CLSIDFromProgID(L"JScript", &clsid ); CoCreateInstance(clsid, 0, CLSCTX_ALL, IID_IActiveScript, (void **)&pAs); pAs->SetScriptSite(this); pAs->QueryInterface(IID_IActiveScriptParse, (void **)&pAsp); pAsp->InitNew(); pAs->AddNamedItem(SimpleMsgBox::GetClassName() ,SCRIPTITEM_GLOBALMEMBERS | SCRIPTITEM_ISVISIBLE); }
スクリプトを実行するRunメソッドをMyScriptSiteクラスに追加します。
IActiveScriptParse::ParseScriptTextを呼び出しスクリプトを解析させ、IActiveScript::SetScriptStateを呼び出して状態をSCRIPTSTATE_CONNECTEDにするとスクリプトが実行されます。
// MyScriptSite クラスに追加 void Run(LPCOLESTR pScript) { pAsp->ParseScriptText(pScript,NULL,NULL,NULL,0,0 ,SCRIPTTEXT_ISPERSISTENT,NULL,NULL); pAs->SetScriptState(SCRIPTSTATE_CONNECTED); }
状態をSCRIPTSTATE_CLOSEDにし、取得したインターフェースを解放するメソッドCloseをMyScriptSiteクラスに追加します。
// MyScriptSite クラスに追加 void Close() { pAs->SetScriptState(SCRIPTSTATE_CLOSED); SAFE_RELEASE(pAs); SAFE_RELEASE(pAsp); }
_tmain関数を次のように書き換えます。スクリプトは直接文字列で渡しています。
int _tmain(int argc, _TCHAR* argv[]) { CoInitialize(NULL); CO_CREATE(MyScriptSite,my_site); my_site->Prepare(); my_site->Run( L"var calc_result=55/89;" L"SimpleMsgBox.Show(\"This is a SimpleMsgBox\");" L"SimpleMsgBox.Show(\"This is a SimpleMsgBox with title\",\"MY SCRIPT\");" L"SimpleMsgBox.Show(calc_result,\"calc_result\");" ); my_site->Close(); SAFE_RELEASE(my_site); CoUninitialize(); return 0; }
以下のようなメッセージボックスが順番に表示されれば成功です。

\"MY SCRIPT\");"
L