我们之前使用的COM组件都是由客户端发起请求,而COM组件提供服务,这样的通信都是单向的,但是有时我们需要双向的通信,比如当一个已经提供服务的COM组件对象触发某个保留的事件时,在前一段时间我们并不知道该怎么做,而现在明确了,但是COM组件已经提供服务,我们对于该事件的处理只能在客户端实现,这时候可连接对象就有了它用武之地了。现在我们说说可连接对象是怎么实现的吧
第一步建立ATL项目,暂时命名为link_obj吧,然后添加ATL简单对象,sobj,这里需要注意的是,要支持连接点,
:想到会自动生成一个_IsobjEvents事件接口,当然还有相应的DIID___IsobjEvents,这个接口就是出接口也就是说,该接口是要我们在客户端实现的接口,为该接口添
加测试成员函数HRESULT Msg,为了添加该成员函数,我们需要将idl以及link_obj_i.h文件中的接口定义都要进行改变,也就是说接口的定义涉及到着两个文件,在idl
中添加
importlib("stdole2.tlb");
[
uuid(9DB001A9-2F50-42A5-BE06-6EC2A8111A58)
]
dispinterface _IsobjEvents
{
properties:
methods:
[id(1)] HRESULT Msg();
};
我们只需要注意添加Msg这一行,
在link_obj_i.h文件中则添加
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("9DB001A9-2F50-42A5-BE06-6EC2A8111A58")
_IsobjEvents : public IDispatch
{
STDMETHOD(Msg)();
};
为了测试,Msg函数要在Advise即连接时执行,那么我们要做的操作就是
STDMETHOD(Advise)(IUnknown *pUnkSink, /* [out] */ __RPC__out DWORD *pdwCookie) { _IsobjEvents*he=static_cast<_isobjevents>(pUnkSink); he->Msg(); return S_OK; }
这样启动生成即可,就得到了.dll文件,现在我们要实现的就是客户端的程序了。
#include<iostream>
using namespace std;
#include"D:\project\link_obj\link_obj\link_obj_i.c"
#include"D:\project\link_obj\link_obj\link_obj_i.h"
class CSkin : public _IsobjEvents
{
public:
CSkin(void){}
~CSkin(void){}
private:
DWORD m_dwRefCount;
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
if (iid == DIID__IsobjEvents)
{
m_dwRefCount++;
*ppvObject = (void *)this;
return S_OK;
}
if (iid == IID_IUnknown)
{
m_dwRefCount++;
*ppvObject = (void *)this;
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef()
{
m_dwRefCount++;
return m_dwRefCount;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG l;
l = m_dwRefCount--;
if ( 0 == m_dwRefCount)
{
delete this;
}
return l;
}
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo)
{
return S_OK;
}
STDMETHOD(Msg)()
{
::MessageBoxA(NULL,"正在执行Msg函数",0,0);
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ __RPC__in REFIID riid,
/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
/* [range][in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
{
return S_OK;
}
/* [local] */ HRESULT STDMETHODCALLTYPE 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)
{
switch(dispIdMember) // 根据不同的dispIdMember,完成不同的回调函数,事件函数的ID编号
{
case 2:
{
// 1st param : [in] long lValue.
}
break;
default: break;
}
return S_OK;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
//创建COM对象实例
IClassFactory*pcf=NULL;
HRESULT hr=::CoGetClassObject(CLSID_sobj,CLSCTX_ALL,NULL,IID_IClassFactory,(void**)&pcf);
Isobj* obj=NULL;
hr=pcf->CreateInstance(NULL,IID_Isobj,(void**)&obj);
pcf->Release();
IConnectionPointContainer*ICPC=NULL;
hr=obj->QueryInterface(IID_IConnectionPointContainer,(void**)&ICPC);
IConnectionPoint*ICP=NULL;
hr=ICPC->FindConnectionPoint(DIID__IsobjEvents,&ICP);
CSkin *psk=new CSkin();
IUnknown* pUk=NULL;
hr=psk->QueryInterface(IID_IUnknown,(void**)&pUk);
DWORD dw;
ICP->Advise(pUk,&dw);
ICP->Unadvise(dw);
::CoUninitialize();
return 0;
}
连接过程可以自己写,也可以使用系统默认的链接函数,均可