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;
}
修改後的外部組件包裝類如下,
第四步:編寫測試代碼