之前讲到一个COM接口可以实现多个连接点。我们就来写个例子。
其实,我还真不知道怎么用ATL向导来实现多个连接点,我们这次就手工改吧。我觉得手工来修改还可以提高对COM的理解,多用用手工还是有好处的。
IDL中增加一个接口_IMyCarEvents2
打开IDL文件,增加以下代码(guid是用windows自带的生成器生成的)
[
uuid(5A745470-8950-4a9b-B624-5D76F1BB28C9)
]
dispinterface _IMyCarEvents2
{
properties:
methods:
[id(1)] HRESULT NeedMoreGas();
};
现在就变成了
library MyComLib
{
importlib("stdole2.tlb");
[
uuid(2CF347A8-63ED-4CE0-8A6D-F98D60C98B8C)
]
dispinterface _IMyCarEvents
{
properties:
methods:
[id(1)] HRESULT OnStop([in] FLOAT Distance);
};
[
uuid(5A745470-8950-4a9b-B624-5D76F1BB28C9)
]
dispinterface _IMyCarEvents2
{
properties:
methods:
[id(1)] HRESULT NeedMoreGas();
};
[
uuid(DA6770F3-CBB6-4F34-A137-2B02A27AB219)
]
coclass MyCar
{
[default] interface IMyCar;
[default, source] dispinterface _IMyCarEvents;
};
};
增加一个proxy类
template<class T>
class CProxy_IMyCarEvents2 :
public ATL::IConnectionPointImpl < T, &__uuidof(_IMyCarEvents2) >
{
public:
HRESULT Fire_NeedMoreGas()
{
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 varResult;
DISPPARAMS params = { NULL, NULL, 0, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}
};
修改对应COM类来支持新的连接点
主要就是:
1. 从新连接点的代理类继承
2. CONNECTION POINT MAP那里加上新的连接点。
class ATL_NO_VTABLE CMyCar :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyCar, &CLSID_MyCar>,
public IConnectionPointContainerImpl<CMyCar>,
public CProxy_IMyCarEvents<CMyCar>,
public CProxy_IMyCarEvents2<CMyCar>, // 支持新的连接点
public IDispatchImpl<IMyCar, &IID_IMyCar, &LIBID_MyComLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
CMyCar()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_MYCAR)
BEGIN_COM_MAP(CMyCar)
COM_INTERFACE_ENTRY(IMyCar)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()
BEGIN_CONNECTION_POINT_MAP(CMyCar)
CONNECTION_POINT_ENTRY(__uuidof(_IMyCarEvents))
CONNECTION_POINT_ENTRY(__uuidof(_IMyCarEvents2)) // 支持新的连接点
END_CONNECTION_POINT_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHOD(Run)();
};
RUN函数触发新连接点的一个事件:
STDMETHODIMP CMyCar::Run()
{
// TODO: Add your implementation code here
this->Fire_OnStop(1000);
this->Fire_NeedMoreGas();
return S_OK;
}
客户端修改
增加一个新的sink类
class CSink2 :
public CComObjectRoot,
public _IMyCarEvents2
{
BEGIN_COM_MAP(CSink2)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(_IMyCarEvents2)
END_COM_MAP()
public:
virtual ~CSink2(){
}
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) { return E_NOTIMPL; }
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { return E_NOTIMPL; }
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { return E_NOTIMPL; }
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
printf("sink, id: %d", dispIdMember);
return S_OK;
}
};
然后挂载
CComObject<CSink2>* sinkptr2 = nullptr;
CComObject<CSink2>::CreateInstance(&sinkptr2);
AtlAdvise(spCar, sinkptr2, __uuidof(_IMyCarEvents2), &cookies);
跑一下,就会发现2个sink对象的Invoke都被调用了,成功。
注意:一个sink类不能处理2个连接点。我试了一下一个sink了继承2个连接点接口,编译都报错了。
错误如下:
1>d:\study\testcom\testcom\testcom.cpp(22): error C2594: 'static_cast' : ambiguous conversions from 'CSink::_ComMapClass *' to 'IDispatch *'
1>d:\study\testcom\testcom\testcom.cpp(81): error C2594: 'argument' : ambiguous conversions from 'ATL::CComObject<CSink> *' to 'IUnknown *'
不知道是我搞错了,还是确实不支持。不过,我觉得一个sink类处理一个连接点还是合理的。不然全放一起,也不好。