标签:
和之前很多实现一样,MFC对可连接对象的支持主要还是依赖基类CCmdTarget。这里贴上上一节我们讲到的实现一个可连接对象需要做的如下:
1.源对象实现IConnnectionPointContainer和IConnectionPoint接口
2.客户端需要实现一个接收器接口,使用时将该接收器连接(注册)到源对象上
3.客户端的接口需要和源对象协商,理论上只要二者商量好即可,但实际中常用的是自动化接口——IDispatch,具体原因请参看《COM原理与应用》
类似对自动化的支持,首先在我们继承CCmdTarget的类构造函数中开启对可连接对象的支持,如下:
EnableConnections();这样,m_xConnPtContainer包含了当前的连接点集合。
注意此时要把连接点容器的接口暴露出来,客户端先找到连接点容器才再找到连接点。这里我们实现的功能是ICat接口调用DoSleep使猫猫睡指定的时间,然后通过ICatEvent连接点通知客户猫猫睡醒了。
对应接口如下
//普通接口定义 BEGIN_INTERFACE_PART(Cat, ICat) INIT_INTERFACE_PART(CAnimalObject, Cat) STDMETHOD_(VOID, DoSleep)(LONG nTime); END_INTERFACE_PART_STATIC(Cat) DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_MAP(CAnimalObject, CCmdTarget) INTERFACE_PART(CAnimalObject, IID_ICat, Cat) INTERFACE_PART(CAnimalObject, IID_IConnectionPointContainer, ConnPtContainer) END_INTERFACE_MAP()
IID_IConnectionPointContainer为标准的连接点容器的IID。
前面说过了,实际中使用的源对象和客户端协商的接口是IDispatch,MFC给出了标准的连接点实现,只需要继承CConnectionPoint即可。使用CConnectionPoint,只需要GetIID传入指定的借口IID,其它的保持默认即可。
同样,这里使用一组宏定义来简化操作,如下:
//连接点接口定义 BEGIN_CONNECTION_PART(CAnimalObject, CatEvent) CONNECTION_IID(IID_ICatEvent) END_CONNECTION_PART(CatEvent) DECLARE_CONNECTION_MAP()
BEGIN_CONNECTION_MAP(CAnimalObject, CCmdTarget) CONNECTION_PART(CAnimalObject, IID_ICatEvent, CatEvent) END_CONNECTION_MAP()这样在客户端即可查询得到可连接接口,具体和接口映射表非常相似。
当我们操作源对象触发指定事件发生时,这时候就会调用连接点,遍历当前连接点上的当前注册的客户接收器,告诉他们这个事件发生,具体实现如下:
//接口实现 STDMETHODIMP_(VOID) CAnimalObject::XCat::DoSleep(LONG nTime) { METHOD_PROLOGUE_EX_(CAnimalObject, Cat) Sleep(nTime); pThis->FiredProcess(nTime); } void CAnimalObject::FiredProcess(LONG nTime) { COleDispatchDriver driver; POSITION position = m_xCatEvent.GetStartPosition(); LPDISPATCH pDispatch; while (position != NULL) { pDispatch = (LPDISPATCH)m_xCatEvent.GetNextConnection(position); ASSERT(pDispatch != NULL); driver.AttachDispatch(pDispatch, FALSE); TRY { driver.InvokeHelper(DISP_ID_WAKE, DISPATCH_METHOD, VT_EMPTY, NULL, PBYTE(VTS_I4), nTime); } END_TRY driver.DetachDispatch(); } }可以看到,这里具体事件触发使用FireProcess完成,这里我们遍历当前的ICatEvent连接点上的注册的接收器,使用Invoke调用通知,MFC具体的实现时注册的接收器保存在m_xCatEvent中。
接收器的实其实就是IDispatch接口的实现,这个在之前我们已经讲过,这里为了方清楚整个过程,还是采用通用的IDisptach实现方式。
接受器的实现并没有统一的标准,我们只需要简单的实现对应接口即可,
这里的生命周期管理实现如下,AddRef和Release直接将引用计数置为1和0.
/************************************************************************/ /* IUnknown生命周期管理,这里并没有统一的实现标准, 所以简单的处理引用计数为1,释放后则计数为0 */ /************************************************************************/ ULONG STDMETHODCALLTYPE CTestComDlg::XCatEvent::AddRef( void) { return 1; } ULONG STDMETHODCALLTYPE CTestComDlg::XCatEvent::Release() { return 0; } HRESULT STDMETHODCALLTYPE CTestComDlg::XCatEvent::QueryInterface( /* [in] */ REFIID iid, /* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObj) { METHOD_PROLOGUE_EX(CTestComDlg, CatEvent) if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDispatch) || IsEqualIID(iid, IID_ICatEvent)) { *ppvObj = this; AddRef(); return S_OK; } else { return E_NOINTERFACE; } }
HRESULT STDMETHODCALLTYPE CTestComDlg::XCatEvent::Invoke( /* [in] */ DISPID dispIdMember, /* [in] */ REFIID riid, /* [in] */ LCID lcid, /* [in] */ WORD wFlags, /* [out][in] */ DISPPARAMS *pDispParams, /* [out] */ VARIANT *pVarResult, /* [out] */ EXCEPINFO *pExcepInfo, /* [out] */ UINT *puArgErr ) { if (DISP_ID_WAKE == dispIdMember) { VARIANT var; var.intVal = 0; if (pDispParams && pDispParams->cArgs==1) { var = (pDispParams->rgvarg)[0]; } CString strInfo; strInfo.Format(L"喵 刚睡了%d毫秒 何事扰朕清修!", var.intVal); AfxMessageBox(strInfo); } else { AfxMessageBox(L"喵~ 干撒子!"); } return S_OK; }
连接到源对象过程是:先查找连接点容器,连接点容器查到连接点,连接到连接点,如下:
//连接 BOOL CTestComDlg::ConnectSource() { BOOL bRet = FALSE; LPCONNECTIONPOINTCONTAINER pConnPtCont = NULL; LPCONNECTIONPOINT pConnPt = NULL; do { if (m_dwCookie!=0 || NULL==m_pDispatch) { break; } //查询连接点容器对象 if (FAILED(m_pDispatch->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&pConnPtCont)) || NULL==pConnPtCont) { break; } //查询连接点 if (FAILED(pConnPtCont->FindConnectionPoint(IID_ICatEvent, &pConnPt)) || NULL==pConnPt) { break; } //连接 DWORD dwCookie = 0; if (FAILED(pConnPt->Advise(&m_xCatEvent, &dwCookie))) { break; } m_dwCookie = dwCookie; bRet = TRUE; } while (FALSE); //可以释放,因为此时处于连接状态,计数不为0 if (pConnPt) { pConnPt->Release(); } if (pConnPtCont) { pConnPtCont->Release(); } return bRet; }
//断开连接 BOOL CTestComDlg::DisConnectSource() { BOOL bRet = FALSE; LPCONNECTIONPOINTCONTAINER pConnPtCont = NULL; LPCONNECTIONPOINT pConnPt = NULL; do { if (m_dwCookie==0 || NULL==m_pDispatch) { break; } //查询连接点容器对象 if (FAILED(m_pDispatch->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&pConnPtCont)) || NULL==pConnPtCont) { break; } //查询连接点 if (FAILED(pConnPtCont->FindConnectionPoint(IID_ICatEvent, &pConnPt)) || NULL==pConnPt) { break; } //断开连接 if (FAILED(pConnPt->Unadvise(m_dwCookie))) { break; } m_dwCookie = 0; bRet = TRUE; } while (FALSE); if (pConnPt) { pConnPt->Release(); } if (pConnPtCont) { pConnPtCont->Release(); } return bRet; }
MFC 实现可连接对象和连接点方法下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
标签:
原文地址:http://blog.csdn.net/wenzhou1219/article/details/52136328