在网上搜Com聚合的例子,发现都比较少,要么是使用ATL,要么是模拟ATL的方式,要么就是模拟MFC的方式,大多偏于原理性的介绍。由于模拟MFC 和真正使用MFC时,在查询IUnknown接口时,流程上有所不同,所以模拟MFC的方式与直接从CCmdTarget派生时,在流程上有较大的差别。即使懂了Com聚合的原理,也可能无法直接使用MFC实现Com 聚合,本人经过一番摸索,终于实现了直接使用MFC实现COM聚合,例子如下:
1.被聚合的组件
1.1 接口声明
#pragma once typedef long HRESULT; // {30DF3430-0266-11cf-BAA6-00AA003E0EED} extern const GUID CLSID_Math; //{ 0x30df3430,0x266,0x11cf,{ 0xba,0xa6,0x0,0xaa,0x3e,0xe,0xed } }; ////////////////////////////////////////////////////////////////////////////////////// // {30DF3432-0266-11cf-BAA6-00AA003E0EED} extern const GUID IID_IOPerator; //{ 0x30df3432,0xed } }; class IOPerator:public IUnknown { public: virtual HRESULT _stdcall Add(int nParam1,int nParam2,int* pResult) =0; virtual HRESULT _stdcall Subtract(int nParam1,int* pResult) =0; virtual HRESULT _stdcall Multiple(int nParam1,int* pResult) =0; virtual HRESULT _stdcall Divide(int nParam1,int* pResult) =0; }; // {30DF3433-0266-11cf-BAA6-00AA003E0EED} extern const GUID IID_IAdvanceOPerator; //{ 0x30df3433,0xed } }; class IAdvanceOPerator:public IUnknown { public: virtual HRESULT _stdcall Abs(int nParam1,int* pResult) =0; virtual HRESULT _stdcall Power(int nParam1,int* pResult) =0; };
// CMyMath command target class CMyMath : public CCmdTarget { DECLARE_DYNCREATE(CMyMath) public: CMyMath(); virtual ~CMyMath(); virtual void OnFinalRelease(); protected: DECLARE_OLECREATE(CMyMath) DECLARE_MESSAGE_MAP() DECLARE_DISPATCH_MAP() DECLARE_INTERFACE_MAP() BEGIN_INTERFACE_PART(OPerator,IOPerator) STDMETHOD_(HRESULT,Add)(int nParam1,int* pResult); STDMETHOD_(HRESULT,Subtract)(int nParam1,Multiple)(int nParam1,Divide)(int nParam1,int* pResult); END_INTERFACE_PART(OPerator) BEGIN_INTERFACE_PART(AdvanceOperator,IAdvanceOPerator) STDMETHOD_(HRESULT,Abs)(int nParam1,Power)(int nParam1,int* pResult); END_INTERFACE_PART(AdvanceOperator) };
1.2 组件实现
#include "stdafx.h" #include "MyCom16.h" #include "MyMath.h" // CMyMath IMPLEMENT_DYNCREATE(CMyMath,CCmdTarget) CMyMath::CMyMath() { EnableAutomation(); EnableAggregation(); } CMyMath::~CMyMath() { } void CMyMath::OnFinalRelease() { // When the last reference for an automation object is released // OnFinalRelease is called. The base class will automatically // deletes the object. Add additional cleanup required for your // object before calling the base class. CCmdTarget::OnFinalRelease(); } BEGIN_MESSAGE_MAP(CMyMath,CCmdTarget) END_MESSAGE_MAP() BEGIN_DISPATCH_MAP(CMyMath,CCmdTarget) END_DISPATCH_MAP() // Note: we add support for IID_IMyMath to support typesafe binding // from VBA. This IID must match the GUID that is attached to the // dispinterface in the .IDL file. // {7259EA0F-0E64-4FF9-BBA1-332E82AFA0D3} static const IID IID_IMyMath = { 0x7259EA0F,0xE64,0x4FF9,{ 0xBB,0xA1,0x33,0x2E,0x82,0xAF,0xA0,0xD3 } }; static const GUID IID_IOPerator = { 0x30df3432,0xed }}; static const GUID IID_IAdvanceOPerator = { 0x30df3433,0xed }}; // CLSID_Math IMPLEMENT_OLECREATE(CMyMath,"MyCom16.MyMath",0x30df3430,0xba,0xed) BEGIN_INTERFACE_MAP(CMyMath,CCmdTarget) INTERFACE_PART(CMyMath,IID_IMyMath,Dispatch) INTERFACE_PART(CMyMath,IID_IOPerator,OPerator) INTERFACE_PART(CMyMath,IID_IAdvanceOPerator,AdvanceOperator) END_INTERFACE_MAP() // CMyMath message handlers ULONG CMyMath::XOPerator::AddRef() { METHOD_PROLOGUE(CMyMath,OPerator); return pThis->ExternalAddRef(); } ULONG CMyMath::XOPerator::Release() { METHOD_PROLOGUE(CMyMath,OPerator); return pThis->ExternalRelease(); } HRESULT CMyMath::XOPerator::QueryInterface(REFIID riid,void** ppObject) { METHOD_PROLOGUE_EX_(CMyMath,OPerator); return pThis->ExternalQueryInterface((void *)&riid,ppObject); } HRESULT CMyMath::XOPerator::Add( int nParam1,int* pResult ) { *pResult = nParam1 + nParam2; return S_OK; } HRESULT CMyMath::XOPerator::Subtract( int nParam1,int* pResult ) { *pResult = nParam1 - nParam2; return S_OK; } HRESULT CMyMath::XOPerator::Multiple( int nParam1,int* pResult ) { *pResult = nParam1 * nParam2; return S_OK; } HRESULT CMyMath::XOPerator::Divide( int nParam1,int* pResult ) { *pResult = nParam1 / nParam2; return S_OK; } ULONG CMyMath::XAdvanceOperator::AddRef() { METHOD_PROLOGUE(CMyMath,AdvanceOperator); return pThis->ExternalAddRef(); } ULONG CMyMath::XAdvanceOperator::Release() { METHOD_PROLOGUE(CMyMath,AdvanceOperator); return pThis->ExternalRelease(); } HRESULT CMyMath::XAdvanceOperator::QueryInterface(REFIID riid,void** ppObject) { METHOD_PROLOGUE(CMyMath,AdvanceOperator); return pThis->ExternalQueryInterface((void *)&riid,ppObject); } HRESULT _stdcall CMyMath::XAdvanceOperator::Abs( int nParam1,int* pResult ) { if(nParam1 < 0) *pResult = -nParam1; else *pResult = nParam1; return S_OK; } HRESULT _stdcall CMyMath::XAdvanceOperator::Power( int nParam1,int* pResult ) { *pResult =1; for(int i=0;i<nParam2;i++) *pResult *=nParam1; return S_OK; }
2 聚合组件
2.1接口声明
#pragma once typedef long HRESULT; class IArea:public IUnknown { public: virtual HRESULT _stdcall Triangle(int width,int High,float* pResult) =0; virtual HRESULT _stdcall Square(int lengh,float* pResult) =0; virtual HRESULT _stdcall Cirle(int r,float* pResult) =0; };
#pragma once #include "IArea.h" // CMyMath2 command target class CMyMath2 : public CCmdTarget { DECLARE_DYNCREATE(CMyMath2) public: CMyMath2(); virtual ~CMyMath2(); virtual void OnFinalRelease(); virtual BOOL OnCreateAggregates(); BEGIN_INTERFACE_PART(Area,IArea) STDMETHOD_(HRESULT,Triangle)(int width,float* pResult); STDMETHOD_(HRESULT,Square)(int lengh,Cirle)(int r,float* pResult); END_INTERFACE_PART(Area) protected: DECLARE_OLECREATE(CMyMath2) DECLARE_MESSAGE_MAP() DECLARE_DISPATCH_MAP() DECLARE_INTERFACE_MAP() };
2.2 接口实现
#include "MyMath2.h" #include <iostream> using namespace std; const float PI = 3.14; static const GUID CLSID_Math = { 0x30df3430,0xed } }; // CMyMath2 IMPLEMENT_DYNCREATE(CMyMath2,CCmdTarget) CMyMath2::CMyMath2() { EnableAutomation(); } CMyMath2::~CMyMath2() { } void CMyMath2::OnFinalRelease() { // When the last reference for an automation object is released // OnFinalRelease is called. The base class will automatically // deletes the object. Add additional cleanup required for your // object before calling the base class. if(m_xInnerUnknown !=NULL) { IUnknown *pUnk =(IUnknown *)m_xInnerUnknown; pUnk->Release(); } CCmdTarget::OnFinalRelease(); } BEGIN_MESSAGE_MAP(CMyMath2,CCmdTarget) END_MESSAGE_MAP() BEGIN_DISPATCH_MAP(CMyMath2,CCmdTarget) END_DISPATCH_MAP() // Note: we add support for IID_IMyMath2 to support typesafe binding // from VBA. This IID must match the GUID that is attached to the // dispinterface in the .IDL file. // {60B1DE57-1DE8-4759-B220-C35E03B2049D} static const IID IID_IMyMath2 = { 0x60B1DE57,0x1DE8,0x4759,{ 0xB2,0x20,0xC3,0x5E,0x3,0xB2,0x4,0x9D } }; static const GUID IID_IArea = { 0x30df3452,0xed }}; // CLSID_Math IMPLEMENT_OLECREATE(CMyMath2,"MyCom9.MyMath2",0x30df3450,0xed) BEGIN_INTERFACE_MAP(CMyMath2,CCmdTarget) INTERFACE_PART(CMyMath2,IID_IMyMath2,Dispatch) INTERFACE_PART(CMyMath2,IID_IArea,Area) INTERFACE_AGGREGATE(CMyMath2,m_xInnerUnknown) //CMyMath2聚合了CMyMath END_INTERFACE_MAP() //CMyMath2聚合了CMyMath BOOL CMyMath2::OnCreateAggregates() { #if 0 //这里是关键,不能这样写 ::CoCreateInstance(CLSID_Math,(IUnknown *)this,CLSCTX_INPROC_SERVER,IID_IUnknown,(void **)&m_xInnerUnknown); #else LPUNKNOWN pUnk = GetControllingUnknown(); ::CoCreateInstance(CLSID_Math,(IUnknown *)pUnk,(void **)&m_xInnerUnknown); #endif return TRUE; } // CMyMath2 message handlers HRESULT _stdcall CMyMath2::XArea::Triangle( int width,float* pResult ) { *pResult =width*High *1.0/2; return S_OK; } HRESULT _stdcall CMyMath2::XArea::Square( int lengh,float* pResult ) { *pResult =lengh *lengh*1.0/2; return S_OK; } HRESULT _stdcall CMyMath2::XArea::Cirle( int r,float* pResult ) { *pResult = PI *r*r; return S_OK; } ULONG CMyMath2::XArea::AddRef() { METHOD_PROLOGUE(CMyMath2,Area); return pThis->ExternalAddRef(); } ULONG CMyMath2::XArea::Release() { METHOD_PROLOGUE(CMyMath2,Area); return pThis->ExternalRelease(); } HRESULT CMyMath2::XArea::QueryInterface(REFIID riid,void** ppObject) { METHOD_PROLOGUE_EX_(CMyMath2,Area); return pThis->ExternalQueryInterface((void *)&riid,ppObject);; }