COM连接点 - 一个COM接口实现多个连接点(3)

时间:2022-04-03 19:45:09

之前讲到一个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类处理一个连接点还是合理的。不然全放一起,也不好。