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; }
以下のようなメッセージボックスが順番に表示されれば成功です。