From: http://blog.csdn.net/zj510/article/details/39080353
之前讲到一个COM接口可以实现多个连接点。我们就来写个例子。
其实,我还真不知道怎么用ATL向导来实现多个连接点,我们这次就手工改吧。我觉得手工来修改还可以提高对COM的理解,多用用手工还是有好处的。
IDL中增加一个接口_IMyCarEvents2
打开IDL文件,增加以下代码(guid是用windows自带的生成器生成的)
- [
- uuid(5A745470-8950-4a9b-B624-5D76F1BB28C9)
- ]
- dispinterface _IMyCarEvents2
- {
- properties:
- methods:
- [id(1)] HRESULT NeedMoreGas();
- };
[
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;
- };
- };
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;
- }
- };
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)();
- };
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;
- }
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;
- }
- };
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);
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类处理一个连接点还是合理的。不然全放一起,也不好。