之前我有一篇博客是介绍如何使用ATL向导生成对象聚合的,那时候实现的快感掩盖了我无知的现实,现在重新拿起COM原理与应用这本书,我想还是全手工自己手写一遍吧,于
是就有了这篇博文,关于对象聚合,我想只是代码复用的一种,如果有两个接口ISome和IOther两个接口分别完成不同的功能,而那么对象聚合的含义就是在外部对象ISome中声
明所有的接口,但是在非ISome的功能函数中调用的是接口IOther的功能函数,而在对象聚合中则是通过变换接口实现的,即根据要使用的功能函数来QueryInterface相应的接口,
并且两者之间要能互相QueryInterface,仅此而已。在ATL中所有的接口都是继承的IDispatch接口,其实一般是没有必要的,我们不如使用midl,继承IUnknown来的简单,首先要
写的是idl文件,进行接口的编译。
import "oaidl.idl"; import "ocidl.idl"; [ object,uuid(CB00819C-7DBF-4AA4-8AB7-3BBEC4944F18),dual,nonextensible,pointer_default(unique) ] interface ISome : IUnknown{ HRESULT some_function(byte* ); }; [ object,uuid(FEB61B82-2B42-447A-B2D8-BC95C2386F01),pointer_default(unique) ] interface IOther : IUnknown{ HRESULT other_function(byte*); }; [ uuid(18A12BCD-A164-490A-8ACA-43897D516FE3),version(1.0),] library objctLib { importlib("stdole2.tlb"); [ uuid(80CC7337-3861-417E-B5E3-16D2AE98C645) ] coclass Some { [default] interface ISome; }; [ uuid(D09FE3C2-97B6-4366-83E7-4BA8E5B5E53D) ] coclass Other { [default] interface IOther; }; }; import "shobjidl.idl";
然后使用VS2010的工具midl进行编译的到5个文件*_i.c和*.h
在新建的控制台程序中加入*.h文件,之后在自行实现继承ISome和IOther的两个类,我的实现如下
#pragma once #include#include #include"taste.h" class AA:public ISome { public: AA(IUnknown* pOuter) { m_pOtherInterface=pOuter; m_Ref=0; } ~AA(void){;} protected: ULONG m_Ref; public: IUnknown*m_pOtherInterface; HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid,/* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) { if(riid==IID_IOther) { return m_pOtherInterface->QueryInterface(riid,ppvObject); } else if(riid==IID_ISome) { *ppvObject=(IUnknown*)this; ((IUnknown*)*ppvObject)->AddRef(); } else if(riid==IID_IUnknown) { *ppvObject=(IUnknown*)this; ((IUnknown*)*ppvObject)->AddRef(); } else return E_NOINTERFACE; return S_OK; } ULONG STDMETHODCALLTYPE AddRef( void) { return m_Ref++; } ULONG STDMETHODCALLTYPE Release( void) { m_Ref--; if(m_Ref==0) { delete this; } return m_Ref; } HRESULT _stdcall some_function(byte*test) { MessageBoxA(NULL,(char*)test,"someFunction",0); return S_OK; } }; class BB:public IOther { public: BB(void) { m_pSomeInterface=NULL; m_Ref=0; } ~BB(void) { ; } protected: ULONG m_Ref; IUnknown* m_pSomeInterface; public: ULONG _stdcall AddRef() { m_Ref++; return m_Ref; } ULONG _stdcall Release() { m_Ref--; if(m_Ref==0) { m_pSomeInterface->Release(); delete this; } return m_Ref; } STDMETHODIMP other_function(byte*test) { ::MessageBoxA(NULL,"otherFunction",0); return S_OK; } HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid,/* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) { if(riid==IID_IOther) { *ppvObject=(IUnknown*)this; ((IUnknown*)*ppvObject)->AddRef(); } else if(riid==IID_IUnknown) { *ppvObject=(IUnknown*)this; ((IUnknown*)*ppvObject)->AddRef(); } else if(riid==IID_ISome) { return m_pSomeInterface->QueryInterface(riid,ppvObject); } return S_OK; } void Init() { //这里传递的应该是BB类的指针还是IOther接口的指针??????,值得思考下 m_pSomeInterface=new AA(this); m_pSomeInterface->QueryInterface(IID_ISome,(void**)&m_pSomeInterface); ////下一步是获得BB中Other接口指针传递给AA,并将其记录下来, } };
各位看官就不要说变量命名规范与否了哈,只是测试,这是客户端的调用
// 43121.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include"hu.h" #include"taste_i.c" int _tmain(int argc,_TCHAR* argv[]) { BB hu; hu.Init(); ISome*ptr=NULL; HRESULT hr=hu.QueryInterface(IID_ISome,(void**)&ptr); ptr->some_function((byte*)"dsad"); IOther* other=NULL; ptr->QueryInterface(IID_IOther,(void**)&other); other->other_function((byte*)"dasda"); other->QueryInterface(IID_ISome,(void**)&ptr); ptr->QueryInterface(IID_IOther,(void**)&other); hu.Release(); }
现在看看其实也只不过是在创建类和被创建的类之间的指针的来回传递,创建类记被穿建的类的指针,同时将自己的this指针传递给被创建的类,同时这俩在QueryInterface进
行不断的变换。在生成的过程中,有时会遇到这样的问题 ,"不能实例化抽象基类",如果是这问题的话,你只需要瞧瞧,是不是有那个纯虚函数还没有实现,我一般问题就是在这儿。这就是即所谓的对象聚合吧