发布时间:2012-12-30 18:19
分类名称:COM
入接口(incoming interface) 接口由COM组件定义,由客户发动调用,提供方是COM组件。
出接口(outgoing interface) 接口由COM组件定义,由COM组件发动调用,提供方是客户。
接收器(sink) 实现COM组件的出接口的COM对象,此对象在客户端实现。
此COM对象不需要CLSID和类厂,其他特征和普通COM对象类似。
可连接对象(connectable object)/ 源对象(Source) 支持一个或者多个出接口的COM对象。
客户和可连接对象的关系图
从图中可以看出来,客户是发起方,1. 客户先和COM建立联系,2. 然后将接收器对象的接口指针传给COM对象。
COM对象和客户俩种对应关系(一对多,多对一)
如何管理可连接对象和接收器的对应关系,以达到通用的目的?
那肯定就是定义一些必要的接口,定义一些必要的功能,双方都依赖接口,实现通用。
可连接对象定义了俩个接口,来管理连接:
IConnectionPointContainer 来管理多个出接口(IConnectionPoint)。
一个IConnectionPoint对应一个接口,IConnectionPoint来管理多个连接。
可连接对象基本结构
IConnectionPointContainer : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints(
/* [out] */ IEnumConnectionPoints **ppEnum) = 0;
virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint(
/* [in] */ REFIID riid,
/* [out] */ IConnectionPoint **ppCP) = 0;
};
IConnectionPoint : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface(
/* [out] */ IID *pIID) = 0;
virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(
/* [out] */ IConnectionPointContainer **ppCPC) = 0;
virtual HRESULT STDMETHODCALLTYPE Advise(
/* [in] */ IUnknown *pUnkSink,
/* [out] */ DWORD *pdwCookie) = 0;
virtual HRESULT STDMETHODCALLTYPE Unadvise(
/* [in] */ DWORD dwCookie) = 0;
virtual HRESULT STDMETHODCALLTYPE EnumConnections(
/* [out] */ IEnumConnections **ppEnum) = 0;
};
可连接对象需要实现这两个接口,然后客户就能使用它们来和COM进行连接通讯。
枚举器
从上面俩个接口中可以看到获取到俩个枚举器:IEnumConnectionPoints , IEnumConnections。而且接口成员基本上是一样的(Out 参数有些区别)。
typedef IConnectionPoint *LPCONNECTIONPOINT;
IEnumConnectionPoints : public IUnknown
{
public:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next(
/* [in] */ ULONG cConnections,
/* [length_is][size_is][out] */ LPCONNECTIONPOINT *ppCP,
/* [out] */ ULONG *pcFetched) = 0;
virtual HRESULT STDMETHODCALLTYPE Skip(
/* [in] */ ULONG cConnections) = 0;
virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Clone(
/* [out] */ IEnumConnectionPoints **ppEnum) = 0;
};
typedef struct tagCONNECTDATA
{
IUnknown *pUnk;
DWORD dwCookie;
} CONNECTDATA;
typedef struct tagCONNECTDATA *PCONNECTDATA;
typedef struct tagCONNECTDATA *LPCONNECTDATA;
IEnumConnections : public IUnknown
{
public:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next(
/* [in] */ ULONG cConnections,
/* [length_is][size_is][out] */ LPCONNECTDATA rgcd,
/* [out] */ ULONG *pcFetched) = 0;
virtual HRESULT STDMETHODCALLTYPE Skip(
/* [in] */ ULONG cConnections) = 0;
virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Clone(
/* [out] */ IEnumConnections **ppEnum) = 0;
};
COM中枚举器的模板为:
class IEnum<ELT_T> : public IUnknown
{
public:
virtual HRESULT Next(
ULONG celt,
ELT_T rgcd,
ULONG *pcFetched) = 0;
virtual HRESULT Skip(ULONG celt) = 0;
virtual HRESULT Reset(void) = 0;
virtual HRESULT Clone(IEnum<ELT_T> **ppenum) = 0;
};
建立连接过程
发起方(操作驱动源):客户。
加上源对象支持出接口ISomeEventSet,客户实现了出接口(ISomeEventSet),指针:pSomeEventSet。
上面的过程出现了三个对象:一个接收器对象,由客户端实现。一个实现了IConnectionPointContainer接口的对象和实现了IConnectionPoint接口对象,由COM对象实现。(实际开发中,使用MFC/ATL开发,框架已经实现好了这俩个对象,我们不用做处理,这就是定义这俩个接口的好处)。
可以看到,整个过程并没有需要提供CLSID和类厂。原因就在于接收器对象是由客户端实现的,并且传给了COM。发起方一直是客户。COM不需要调用诸如CoCreateInstance去和客户通讯创建对象。代码实现:
ISomeEventSet *gpSomeEventSet;
… …
// Initialize
CSomeEventSet *pSink = new CSomeEventSet;
pSink->QueryInterface(IID_ISomeEventSet, pSomeEventSet);
… …
// connect the sink object to the connectable object we have
hr = pConnectionPoint->Advise(pSomeEvent, &pSomeEvent->m_dwCookie);
… …
// disconnect the sink object from the connectable object we have
hr = pConnectionPoint->Unadvise(pSomeEvent->m_dwCookie);
… …
pSink->Release();
事件激发和处理
通常,事件激发有"源对象(可连接对象)"的入接口激发。
问题:以上都是客户和COM预先约定好出接口。如果客户和COM是分开开发的,客户并不知道COM支持什么出接口,如何处理?
可以通过IConnectionPointContainer和IConnectionPoint俩个接口,只能得到COM出接口的IID。并得不到出接口的声明信息。即便可能得到接口信息,还需要根据接口信息动态创建出对象,然后建立连接,这些操作并不简单。现在难点有俩个:
获取接口信息,客户首先向源对象请求IProvideClassInfo接口,获得接口指针,调用GetClassInfo,获得ITypeInfo接口指针。
IProvideClassInfo : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetClassInfo(
/* [out] */ ITypeInfo **ppTI) = 0;
};
ITypeInfo : public IUnknown
{
public:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetTypeAttr(
/* [out] */ TYPEATTR **ppTypeAttr) = 0;
virtual HRESULT STDMETHODCALLTYPE GetTypeComp(
/* [out] */ ITypeComp **ppTComp) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetFuncDesc(
/* [in] */ UINT index,
/* [out] */ FUNCDESC **ppFuncDesc) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetVarDesc(
/* [in] */ UINT index,
/* [out] */ VARDESC **ppVarDesc) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetNames(
/* [in] */ MEMBERID memid,
/* [length_is][size_is][out] */ BSTR *rgBstrNames,
/* [in] */ UINT cMaxNames,
/* [out] */ UINT *pcNames) = 0;
virtual HRESULT STDMETHODCALLTYPE GetRefTypeOfImplType(
/* [in] */ UINT index,
/* [out] */ HREFTYPE *pRefType) = 0;
virtual HRESULT STDMETHODCALLTYPE GetImplTypeFlags(
/* [in] */ UINT index,
/* [out] */ INT *pImplTypeFlags) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [size_is][in] */ LPOLESTR *rgszNames,
/* [in] */ UINT cNames,
/* [size_is][out] */ MEMBERID *pMemId) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
/* [in] */ PVOID pvInstance,
/* [in] */ MEMBERID memid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetDocumentation(
/* [in] */ MEMBERID memid,
/* [out] */ BSTR *pBstrName,
/* [out] */ BSTR *pBstrDocString,
/* [out] */ DWORD *pdwHelpContext,
/* [out] */ BSTR *pBstrHelpFile) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetDllEntry(
/* [in] */ MEMBERID memid,
/* [in] */ INVOKEKIND invKind,
/* [out] */ BSTR *pBstrDllName,
/* [out] */ BSTR *pBstrName,
/* [out] */ WORD *pwOrdinal) = 0;
virtual HRESULT STDMETHODCALLTYPE GetRefTypeInfo(
/* [in] */ HREFTYPE hRefType,
/* [out] */ ITypeInfo **ppTInfo) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE AddressOfMember(
/* [in] */ MEMBERID memid,
/* [in] */ INVOKEKIND invKind,
/* [out] */ PVOID *ppv) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateInstance(
/* [in] */ IUnknown *pUnkOuter,
/* [in] */ REFIID riid,
/* [iid_is][out] */ PVOID *ppvObj) = 0;
virtual HRESULT STDMETHODCALLTYPE GetMops(
/* [in] */ MEMBERID memid,
/* [out] */ BSTR *pBstrMops) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetContainingTypeLib(
/* [out] */ ITypeLib **ppTLib,
/* [out] */ UINT *pIndex) = 0;
virtual /* [local] */ void STDMETHODCALLTYPE ReleaseTypeAttr(
/* [in] */ TYPEATTR *pTypeAttr) = 0;
virtual /* [local] */ void STDMETHODCALLTYPE ReleaseFuncDesc(
/* [in] */ FUNCDESC *pFuncDesc) = 0;
virtual /* [local] */ void STDMETHODCALLTYPE ReleaseVarDesc(
/* [in] */ VARDESC *pVarDesc) = 0;
};
使用IDispath作为出接口的模型