com聚合是包容的一种特例,目的是最大化的实现功能模块话,降低组件间的耦合性。
第一步:
通过ATL创建com外部组件TestOutCom.dll,在其内部创建接口包装类CCMyOuterCom,并实现其对应的接口ICMyOuterCom,注意默认选择“聚合”(这点很重要,后面我们会详细解说)
在“聚合”选项中:
勾选“是”,则按照默认生成支持聚合的包装类
勾选“否”,则包装类头文件CMyOuterCom.h,在公开接口列表字段(“BEGIN_COM_MAP”)前面会出现 “DECLARE_NOT_AGGREGATABLE(CCMyOuterCom)”
勾选“只能创建为聚合”,则包装类头文件CMyOuterCom.h,在公开接口列表字段(“BEGIN_COM_MAP”)前面会出现 “ “DECLARE_ONLY_AGGREGATABLE(CCMyOuterCom)”
和聚合相关的宏有三个:
DECLARE_NOT_AGGREGATABLE //不支持聚合
DECLARE_AGGREGATABLE //支持聚合
DECLARE_ONLY_AGGREGATABLE //仅支持聚合
三个宏定义如下:
- #define DECLARE_NOT_AGGREGATABLE(x) public:/
- typedef ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< x > >, ATL::CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass;
- #define DECLARE_AGGREGATABLE(x) public:/
- typedef ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< x > >, ATL::CComCreator< ATL::CComAggObject< x > > > _CreatorClass;
- #define DECLARE_ONLY_AGGREGATABLE(x) public:/
- typedef ATL::CComCreator2< ATL::CComFailCreator<E_FAIL>, ATL::CComCreator< ATL::CComAggObject< x > > > _CreatorClass;
通过宏的定义可以清楚的看到组件的创建过程。
如果组件不支持聚合,那么T2就是 CComFailCreator<CLASS_E_NOAGGREGATION>。若想创建被聚合情况下的组件,那么就会调用 CComFailCreator::CreateInstance(),然后直接返回参数CLASS_E_NOAGGREGATION。同样,如果组件仅支持聚合,那么T1就是CComFailCreator<E_FAIL>,创建时直接返回E_FAIL。
CComAggObject提供了两个IUnknown的实现。一个实现用于转发调用给外部的控制对象,包含它的生命期和身份标识符,另一个用于实现外部控制对象的私有用途,用于维护内部对象的生命期和接口查询。CComAggObject拥有IUnknown接口的两套实现方式,一种是通过CComObjectRootEx直接继承获得,一种是通过成员变量CComContainedObject<contained> m_contained间接获得。m_contained变量用于维护m_pOuterUnknown成员。
另外也可以使用CComPloyObject来支持组件聚合。
之后点击“完成”;并在接口中实现“GetOuterMethord”方法,方便后期测试和使用。当然也可以加入自己项目中需要的函数
完成接口方法的创建,此时我们的外部组件就创建成功了。
第二步:
按照第一步的方法,同样通过ATL创建com建内部组件TestInnerCom.dll,在其内部创建接口包装类CCMyInnerCom,并实现其对应的接口ICMyInnerCom,在ICMyInnerCom实现实现“GetInnerMethord”方法。
最后两个组件中包装类的结构分别如下
第三步:
为了实现聚合操作,需要对外部组件的包装类的头文件CMyOuterCom.h进行修改。而对内部组件不需要进行任何操作。
加入对内部组件接口信息的文件引用:
#include "..\TestInnerCom\TestInnerCom_i.h"
#include "..\TestInnerCom\TestInnerCom_i.c"
增加变量IUnknown * m_InnerComUnknown;
修改组件对客户程序公布的接口列表:
BEGIN_COM_MAP(CCMyOuterCom)
COM_INTERFACE_ENTRY(ICMyOuterCom)
COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_InnerComUnknown) //声明聚合,只有公开了外面才能通过IUnknown
指针来访问内部组件的接口
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
修改外部组件包装类的接口构造函数:
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
HRESULT hr = CoCreateInstance(CLSID_CMyInnerCom,
this,//GetControllingUnknown(),
CLSCTX_ALL,
IID_IUnknown,
(void**)&m_InnerComUnknown);
if (FAILED(hr))
{
return E_FAIL ;
}
return S_OK;
}
修改后的外部组件包装类如下,
第四步:编写测试代码