我不久前创建了一个ATL COM服务器组件(exe)。它暴露了一些普通的COM API(派生自IDispatch),并且还触发了一些COM事件。事件机制是使用ATL IConnectionPointContainer实现的。这个COM服务器最初由一个简单的C#应用程序使用,它直接添加了对COM服务器的引用。一切,API和事件,在C#app中运行良好。
然后要求COM服务器能够在网页(IE)中使用javascript。因此,我将IProvideClassInfo2,IObjectSafety实现添加到原始COM类。但是,COM事件从未奏效。请参阅下面的IDL,COM类头文件和事件触发代码。
IDL:
import "oaidl.idl";
import "ocidl.idl";
[
object,
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000000),
dual,
nonextensible,
helpstring("ICtrl Interface"),
pointer_default(unique)
]
interface ICtrl : IDispatch{
[id(1), helpstring("method CtrlMethod1")]
HRESULT CtrlMethod1(void);
[id(2), helpstring("method CtrlMethod2")]
HRESULT CtrlMethod2([in] ULONG Reason);
};
[
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000001),
version(1.0),
]
library MyControlLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000002)
]
dispinterface _ICtrlEvents
{
properties:
methods:
[id(1), helpstring("method OnCtrlEvent1")]
HRESULT OnCtrlEvent1([in] LONG ErrorCode);
[id(2), helpstring("method OnCtrlEvent2")]
HRESULT OnCtrlEvent2([in] LONG ErrorCode);
};
[
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000003)
]
coclass Ctrl
{
[default] interface ICtrl;
[default, source] dispinterface _ICtrlEvents;
};
};
COM类标题:
// CCtrl
class ATL_NO_VTABLE CCtrl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCtrl, &CLSID_Ctrl>,
public IConnectionPointContainerImpl<CCtrl>,
public CProxy_ICtrlEvents<CCtrl>,
public IDispatchImpl<ICtrl, &IID_ICtrl, &LIBID_MyControlLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IDispatchImpl<_ICtrlEvents, &__uuidof(_ICtrlEvents), &LIBID_MyControlLib, /* wMajor = */ 1, /* wMinor = */ 0>,
public IObjectSafetyImpl<CCtrl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>,
public IProvideClassInfo2Impl<&CLSID_Ctrl, NULL, &LIBID_MyControlLib>
{
public:
DECLARE_CLASSFACTORY_SINGLETON(CCtrl)
CCtrl();
DECLARE_REGISTRY_RESOURCEID(IDR_CTRL)
BEGIN_COM_MAP(CCtrl)
COM_INTERFACE_ENTRY(ICtrl)
COM_INTERFACE_ENTRY2(IDispatch, ICtrl)
COM_INTERFACE_ENTRY2(IDispatch, _ICtrlEvents)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(_ICtrlEvents)
COM_INTERFACE_ENTRY(IObjectSafety)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()
BEGIN_CONNECTION_POINT_MAP(CCtrl)
CONNECTION_POINT_ENTRY(__uuidof(_ICtrlEvents))
END_CONNECTION_POINT_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct();
void FinalRelease();
public:
STDMETHOD(CtrlMethod1)(void);
STDMETHOD(CtrlMethod2)(ULONG Reason);
};
OBJECT_ENTRY_AUTO(__uuidof(Ctrl), CCtrl)
ATL生成的事件触发代码:
#pragma once
template<class T>
class CProxy_ICtrlEvents :
public ATL::IConnectionPointImpl<T, &__uuidof(_ICtrlEvents)>
{
public:
HRESULT OnCtrlEvent1(LONG ErrorCode)
{
HRESULT hr = S_OK;
T * pThis = static_cast<T *>(this);
int cConnections = m_vec.GetSize();
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();
IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
if (pConnection)
{
CComVariant avarParams[1];
avarParams[0] = ErrorCode;
avarParams[0].vt = VT_I4;
CComVariant varResult;
DISPPARAMS params = { avarParams, NULL, 1, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}
HRESULT Fire_OnCtrlEvent2(LONG ErrorCode)
{
HRESULT hr = S_OK;
T * pThis = static_cast<T *>(this);
int cConnections = m_vec.GetSize();
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();
IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
if (pConnection)
{
CComVariant avarParams[1];
avarParams[0] = ErrorCode;
avarParams[0].vt = VT_I4;
CComVariant varResult;
DISPPARAMS params = { avarParams, NULL, 1, 0 };
hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}
};
在javascript代码中,使用
创建COM对象var CtrlObj = new ActiveXObject('ProgID_of_Ctrl')
'ProgID_of_Ctrl'映射到__uuidof(Ctrl)。在IE调试器中,创建的对象是ICtrl类型。 COM API是可见的,但COM事件不可见。任何使用CtrlObj.attachEvent()的尝试都将导致javascript错误。我希望CtrlObj应该是coclass(Ctrl)类型,就像C#app一样。 COM_MAP部分中是否有任何错误?任何评论和帮助表示赞赏。
-CodeFarmer
答案 0 :(得分:1)
根据我的阅读,您应该使用OBJECT
标记和SCRIPT for
标记来连接HTML中的ATL / COM事件。像这样:
<object
id="myCtrlObj"
classid="CLSID:00000000-0000-0000-0000-000000000003"
height="32"
width="32"/>
<script language="javascript"
id="myCtrlHandler1"
event="OnCtrlEvent1()"
for="myCtrlObj">
alert("OnCtrlEvent1 fired");
</script>
<script language="javascript"
id="myCtrlHandler2"
event="OnCtrlEvent2(reason)"
for="myCtrlObj">
alert("OnCtrlEvent2 fired with parameter: " + reason.toString());
</script>
因为你正在使用JScript,我有时想作弊并使IDispatch VARIANT属性模拟事件的行为。在下面的JScript代码片段中,注意如何将OnCtrlEvent1和OnCtrlEvent2分配给函数:
function tst()
{
var ctrl = new ActiveXObject("MyControl.Ctrl");
ctrl.OnCtrlEvent1 = myevent1;
ctrl.OnCtrlEvent2 = myevent2;
ctrl.CtrlMethod1();
ctrl.CtrlMethod2();
}
function myevent1()
{
alert("Event1");
}
function myevent2(reason)
{
alert("Event2 " + reason.toString());
}
通过将其作为IDL中的属性处理来完成欺骗。将那些JScript函数作为包含可调用IDispatch接口的VARIANT传递给我们。这是我的MyControl.idl:
import "oaidl.idl";
import "ocidl.idl";
[
object,
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000000)
dual,
nonextensible,
helpstring("ICtrl Interface"),
pointer_default(unique)
]
interface ICtrl : IDispatch{
[id(1), helpstring("method CtrlMethod1")] HRESULT CtrlMethod1(void);
[id(2), helpstring("method CtrlMethod2")] HRESULT CtrlMethod2(void);
[propget, id(3), helpstring("property OnCtrlEvent1")] HRESULT OnCtrlEvent1([out, retval] VARIANT* pVal);
[propput, id(3), helpstring("property OnCtrlEvent1")] HRESULT OnCtrlEvent1([in] VARIANT newVal);
[propget, id(4), helpstring("property OnCtrlEvent2")] HRESULT OnCtrlEvent2([out, retval] VARIANT* pVal);
[propput, id(4), helpstring("property OnCtrlEvent2")] HRESULT OnCtrlEvent2([in] VARIANT newVal);
};
[
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000001),
version(1.0),
helpstring("MyControl 1.0 Type Library")
]
library MyControlLib
{
importlib("stdole2.tlb");
[
// uuid replaced with dummy
uuid(00000000-0000-0000-0000-000000000003)
helpstring("Ctrl Class")
]
coclass Ctrl
{
[default] interface ICtrl;
};
};
这是我的Ctrl.h,您可以在其中看到JScript函数将保存在VARIANT成员中:
class ATL_NO_VTABLE CCtrl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCtrl, &CLSID_Ctrl>,
public IDispatchImpl<ICtrl, &IID_ICtrl, &LIBID_MyControlLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_CTRL)
BEGIN_COM_MAP(CCtrl)
COM_INTERFACE_ENTRY(ICtrl)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHOD(CtrlMethod1)(void);
STDMETHOD(CtrlMethod2)(void);
STDMETHOD(get_OnCtrlEvent1)(VARIANT* pVal);
STDMETHOD(put_OnCtrlEvent1)(VARIANT newVal);
STDMETHOD(get_OnCtrlEvent2)(VARIANT* pVal);
STDMETHOD(put_OnCtrlEvent2)(VARIANT newVal);
private:
CComVariant m_ctrlEvent1;
CComVariant m_ctrlEvent2;
STDMETHOD(Invoke_CtrlEvent1)();
STDMETHOD(Invoke_CtrlEvent2)(LONG nReason);
};
OBJECT_ENTRY_AUTO(__uuidof(Ctrl), CCtrl)
在Ctrl.cpp中,我们在那些VARIANT中寻找JScript函数的IDispatch接口,并且通过我们对参数的“知识”,我们使用正确的参数调用每个事件:
#include "stdafx.h"
#include "Ctrl.h"
STDMETHODIMP CCtrl::CtrlMethod1(void)
{
Invoke_CtrlEvent1();
return S_OK;
}
STDMETHODIMP CCtrl::CtrlMethod2(void)
{
Invoke_CtrlEvent2(12345);
return S_OK;
}
STDMETHODIMP CCtrl::get_OnCtrlEvent1(VARIANT* pVal)
{
VariantInit(pVal);
return VariantCopy(pVal, &m_ctrlEvent1);
return S_OK;
}
STDMETHODIMP CCtrl::put_OnCtrlEvent1(VARIANT newVal)
{
m_ctrlEvent1 = newVal;
return S_OK;
}
STDMETHODIMP CCtrl::get_OnCtrlEvent2(VARIANT* pVal)
{
VariantInit(pVal);
return VariantCopy(pVal, &m_ctrlEvent2);
}
STDMETHODIMP CCtrl::put_OnCtrlEvent2(VARIANT newVal)
{
m_ctrlEvent2 = newVal;
return S_OK;
}
STDMETHODIMP CCtrl::Invoke_CtrlEvent1()
{
if (m_ctrlEvent1.vt != VT_DISPATCH)
{
return S_OK;
}
DISPPARAMS DispParams = { 0, 0, 0, 0 };
VARIANT Var = { 0 };
return V_DISPATCH(&m_ctrlEvent1)->Invoke((DISPID) 0, IID_NULL, 0, DISPATCH_METHOD, &DispParams, &Var, NULL, NULL);
}
STDMETHODIMP CCtrl::Invoke_CtrlEvent2(LONG nReason)
{
if (m_ctrlEvent1.vt != VT_DISPATCH)
{
return S_OK;
}
VARIANTARG Arg = {0};
Arg.vt = VT_I4;
Arg.lVal = nReason;
DISPPARAMS DispParams = { &Arg, 0, 1, 0 };
VARIANT Var = { 0 };
return V_DISPATCH(&m_ctrlEvent2)->Invoke((DISPID) 0, IID_NULL, 0, DISPATCH_METHOD, &DispParams, &Var, NULL, NULL);
}