COMサーバーにJScriptのイベントハンドラを登録する の「修正版」です。
前回の例には、致命的なエラーが起こる場合があります。onCurrent というプロパティーに関数オブジェクトを設定し、取得することができます。しかし、まだonCurrentプロパティに関数オブジェクトを設定しない、未設定の場合、onCurrent を参照すると、スクリプト側にエラー値を戻すとので、エラーがスローされ実行が止まってしまいます。
つまり、
var obj = new ActiveXObject("MyServer.Utility");
WScript.Echo(obj.onCurrent);
とすると、エラーで止まってしまいます。まだプロパティが未設定ならば、nullを返すようにすれば万事OKです(^^;
というわけで、修正です。
まず、onCurrentのプロパティの設定と取得でのデータ受け渡しを、IDispatch* ではなく、VARIANT にします。関数オブジェクトは、このVARIANTで、vtメンバをVT_DISPATCHにして受け渡すことになります。
ところどころ修正したものが次のとおり。
/*
compiler command line :
cl /LD MyServer.cpp /link /TLBOUT:MyServer.tlb
*/
#pragma comment(lib,"comsuppw.lib")
#define STRICT
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#define _ATL_ATTRIBUTES
#define _ATL_APARTMENT_THREADED
#define _ATL_NO_AUTOMATIC_NAMESPACE
#include <atlbase.h>
#include <atlcom.h>
#include <atlwin.h>
#include <atltypes.h>
#include <atlctl.h>
#include <atlhost.h>
using namespace ATL;
#include <comutil.h>
//マクロ定義
#define PPVOID(X) (reinterpret_cast<LPVOID*>(X))
#define QI(X,Y,Z) ((X)->QueryInterface((Y),(reinterpret_cast<LPVOID*>(Z))))
#define RELEASE(X) {if(X){(X)->Release(); X=NULL;}}
// DllMain,DllCanUnloadNow,DllRegisterServer and DllUnregisterServer
[ module(dll, name = "MyServer", helpstring = "MyServer 1.0 Type Library") ];
[ emitidl ];
/////////////////////////////////////////////////////////////////////////////
// インターフェイス定義 : IMyServer
[
object,
dual,
oleautomation,
helpstring("IMyServer Interface"),
pointer_default(unique)
]
__interface IMyServer : IDispatch
{
[id(1)] HRESULT CurrentDirectory([out,retval]BSTR *sPath);
[id(2),propput] HRESULT onCurrent([in] VARIANTARG var);
[id(2),propget] HRESULT onCurrent([out,retval] VARIANTARG *pvar);
};
/////////////////////////////////////////////////////////////////////////////
// イベントインターフェイス定義 : IMyServerEvent
[
dispinterface,
hidden,
helpstring("IMyServerEvents Interface")
]
__interface IMyServerEvent
{
[id(1)] HRESULT onCurrent();
};
/////////////////////////////////////////////////////////////////////////////
// インターフェイス実装 : CMyServer
[
coclass,
threading(apartment),
source(IMyServerEvent),
event_source(com),
vi_progid("MyServer.Utility"),
progid("MyServer.Utility.1"),
version(1.0),
helpstring("MyServer Class")
]
class ATL_NO_VTABLE CMyServer :
public IMyServer
{
public:
CMyServer()
{
}
__event __interface IMyServerEvent;
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
m_pDispatch = NULL;
return S_OK;
}
void FinalRelease()
{
RELEASE(m_pDispatch);
return;
}
HRESULT CurrentDirectory(BSTR *sPath)
{
TCHAR pBuffer[MAX_PATH+1] = {0};
::GetCurrentDirectory(MAX_PATH+1,pBuffer);
*sPath = _bstr_t(pBuffer).Detach();
Fire();
return S_OK;
}
// スクリプトから渡されるオブジェクトの設定と取得
HRESULT put_onCurrent(VARIANTARG var)
{
HRESULT hRes = E_INVALIDARG;
LPDISPATCH pDispatchNew = NULL;
if(var.vt == VT_DISPATCH)
{
if(SUCCEEDED(hRes = QI(var.pdispVal,IID_IDispatch,&pDispatchNew)))
{
/*if(m_pDispatch)
m_pDispatch->Release();*/
RELEASE(m_pDispatch);
m_pDispatch = pDispatchNew;
}
}
else
hRes = E_INVALIDARG;
return hRes;
}
//関数オブジェクトの取得
HRESULT get_onCurrent(VARIANTARG *pvar)
{
HRESULT hRes = S_OK;
IDispatch *pDisp = NULL;
if(m_pDispatch)
{
if(SUCCEEDED(hRes = QI(m_pDispatch,IID_IDispatch,&pDisp)))
{
VariantClear(pvar);
pvar->vt = VT_DISPATCH;
pvar->pdispVal = pDisp;
}
}
else
{
//もし保持している関数オブジェクトがなければ空に。
VariantClear(pvar);
}
return hRes;
}
private:
LPDISPATCH m_pDispatch;
void Fire()
{
DISPPARAMS dispparams = {NULL,NULL,0,0};
//COMイベント発火
IMyServerEvent_onCurrent();
//設定された関数オブジェクトを実行,デフォルト
if(m_pDispatch)
m_pDispatch->Invoke((DISPID)DISPID_VALUE,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dispparams,
NULL,
NULL,
NULL);
}
};
VARIANT型とIDispatchインターフェイスは、C++とJScriptとでデータを受け渡しする上で必ず付いてまわるのですが、あまりこのあたりを詳しく解説してある本を見つけることができないのがネックです。
この件の備忘録終了。