和之前很多实现一样,MFC对可连接对象的支持主要还是依赖基类CCmdTarget。这里贴上上一节我们讲到的实现一个可连接对象需要做的如下:
1.源对象实现IConnnectionPointContainer和IConnectionPoint接口
2.客户端需要实现一个接收器接口,使用时将该接收器连接(注册)到源对象上
3.客户端的接口需要和源对象协商,理论上只要二者商量好即可,但实际中常用的是自动化接口——IDispatch,具体原因请参看《COM原理与应用》
1.实现连接点容器
类似对自动化的支持,首先在我们继承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。
2.实现连接点
前面说过了,实际中使用的源对象和客户端协商的接口是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()
这样在客户端即可查询得到可连接接口,具体和接口映射表非常相似。
3.触发操作
当我们操作源对象触发指定事件发生时,这时候就会调用连接点,遍历当前连接点上的当前注册的客户接收器,告诉他们这个事件发生,具体实现如下:
//接口实现
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中。
4.接收器的实现
接收器的实其实就是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;
}
5.连接到源对象和断开连接
连接到源对象过程是:先查找连接点容器,连接点容器查到连接点,连接到连接点,如下:
//连接
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