久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

COM聚合

 semo_zhang 2013-12-06
   
 
聚合的概念
聚合源自組件重用,。當(dāng)有兩個(gè)組件A和B,他們分別實(shí)現(xiàn)了自己的接口IA和IB,。如果有一個(gè)客戶程序創(chuàng)建了A對(duì)象使得自己可以調(diào)用IA的方法,,但同時(shí)又想獲得IB的接口,調(diào)用IB的方法,。這時(shí)候有兩種做法:一種是客戶程序創(chuàng)建B對(duì)象,,還有一種方法是A組件內(nèi)部創(chuàng)建B組件,然后客戶通過(guò)某種途徑調(diào)用B的接口方法,。
第一種方法,,使得客戶必須知道有獨(dú)立的B組件的存在,第二種方法客戶可以認(rèn)為只有一個(gè)組件A,,組件A實(shí)現(xiàn)了兩個(gè)接口IA和IB,。第二種方法可以制造出一種假象,讓客戶程序編寫(xiě)更加簡(jiǎn)單,。從組件A如何管理組件B的方法上,,第二種方法還可以分為兩種:包容和聚合。
包容很簡(jiǎn)單,,如果組件IB接口擁有一個(gè)方法F(),那么A組件就要實(shí)現(xiàn)一個(gè)自己的IBEx接口,,并實(shí)現(xiàn)IBEx::F( )方法,內(nèi)部調(diào)用IB::F()方法,。這樣,,客戶也就可以通過(guò)調(diào)用IBEx::F()來(lái)調(diào)用IB::F。在這種情況下,,客戶只知道有IA和IBEx接口,,不知道還存在另一個(gè)B組件和IB接口。IBEx::F()增加一些代碼從而修改IB::F()方法的功能,,甚至可以完全丟棄IB::F()方法,。
聚合通常用于IB接口的功能完全不需要做任何的修改,就可以直接交給用戶使用的情況,。這時(shí)候,,如果IB接口的方法很多,包容就顯得很笨拙,。因?yàn)樗坏貌粚?duì)每一個(gè)方法作一次包裝,,盡管什么都不做。COM+對(duì)象池就是通過(guò)聚合我們的組件,來(lái)把我們組件的接口暴露給客戶的,。聚合方式下,,A組件直接將IB接口交給客戶,客戶就可以調(diào)用,,但是客戶仍然以為是A組件實(shí)現(xiàn)了IB接口,。如下圖:
                              組件A
                                      IUnknown

       組件B
IB
 

 
 
 

客戶直接使用
 
 

IA
 


客戶直接使用
 
內(nèi)部組件的實(shí)現(xiàn)
       客戶程序只知道A組件而不知道B組件,并且認(rèn)為A組件實(shí)現(xiàn)了IA和IB接口,。因此,,當(dāng)客戶創(chuàng)建了A組件(通過(guò)CoCreateInstance函數(shù)),獲取到IUnknown接口時(shí),,應(yīng)該獲得的是A組件實(shí)現(xiàn)的IUnknown接口,。      問(wèn)題在于B組件有自己的IUnknown的接口實(shí)現(xiàn),如果B組件還是采用一般的方法實(shí)現(xiàn)IUnknown接口的話,,當(dāng)客戶調(diào)用IB::QueryInterface函數(shù),,就不會(huì)得到IA接口,這當(dāng)然是不允許的,。所以一個(gè)支持聚合的組件,,它的IUnknown實(shí)現(xiàn)必然要有別于普通組件。
       B組件應(yīng)該擁有一個(gè)成員變量IUnknown* m_pUnknownOuter;該指針可以指向A組件的IUnknown接口,。當(dāng)客戶調(diào)用IB::QueryInterface請(qǐng)求時(shí),,如果IB接口已經(jīng)被聚合了,就調(diào)用m_pUnknownOuter->QueryInterface方法,,這實(shí)際上就是調(diào)用了A組件的QueryInterface方法,。那么,,m_pUnknownOuter指針是什么時(shí)候被初始化的呢,?CoCreateInstance函數(shù)和IClassFactory::CreateInstance方法都接受一個(gè)IUnknown參數(shù)。如果A組件內(nèi)部想聚合B組件的IB接口,,他就會(huì)將自己的IUnknown指針傳遞進(jìn)去,如果A組件并不想聚合B組件,,那么簡(jiǎn)單的傳遞一個(gè)NULL就行了。
       B組件可以根據(jù)m_pUnknownOuter是否為NULL,,來(lái)判斷是否被聚合,。
       B組件實(shí)現(xiàn)的具體步驟如下:
1) 聲明一個(gè)INondelegationUnknown接口,該接口擁有IUnknown接口一樣的純虛函數(shù),,當(dāng)然函數(shù)名前面均加上Nondelegation前綴,。
2) CB類(假設(shè)CB類為組件類)繼承并實(shí)現(xiàn)INondelegationUnknown接口,實(shí)現(xiàn)代碼和普通組件的IUnknown一樣,,這用于非聚合的情況下,。
3) CB類的構(gòu)造函數(shù)接受IUnknown指針,如果傳遞進(jìn)來(lái)的是NULL,說(shuō)明B組件并不被用作聚合。m_pUnknownOuter變量就指向B組件自身的IUnknown接口,。如果傳遞進(jìn)來(lái)的不是NULL,,說(shuō)明被用作聚合。m_pUnknownOuter變量值等于參數(shù)值,,指向外部組件的IUnknown接口指針,。
4) CB類也要繼承并實(shí)現(xiàn)IUnknown接口。這個(gè)接口的QueryInterface函數(shù)實(shí)現(xiàn)只是調(diào)用m_pUnknownOuter->QueryInterfac方法,。m_pUnknownOuter究竟代表什么取決于創(chuàng)建組件時(shí)是否傳遞了外部組件的IUnknown指針,。
5) B組件的類廠的CreateInstance方法內(nèi)部創(chuàng)建CB類時(shí),將IUnknown* pUnkownOuter參數(shù)傳遞給構(gòu)造函數(shù),。創(chuàng)建成功后,,調(diào)用B組件自身的(而不是外部的)NondelegationQueryInterface方法,將自身的INondelegationUnknown傳遞出去給組件A,。
 
 
外部組件的實(shí)現(xiàn)
       外部組件A要?jiǎng)?chuàng)建組件B的實(shí)例,,并保存B組件的INondelegationUnknown接口指針。該接口指針是通過(guò)上一節(jié)5)由B的類廠返回的,。m_pUnknownInner變量保存了B接口的INondelegationUnknown接口指針,。外部組件調(diào)用CoCreateInstance函數(shù)創(chuàng)建聚合組件時(shí),iid必須等于IID_IUnknown,。
       外部組件要修改自己的QueryInterface,,當(dāng)客戶程序請(qǐng)求IB接口的時(shí)候,將調(diào)用m_pUnknownInner->QueryInterface方法,。
       外部組件可以通過(guò)QueryInterface的代碼來(lái)控制是否聚合B組件所有的接口,。如果不想聚合B組件實(shí)現(xiàn)的IC接口??梢栽谳斎?yún)?shù)iid等于IID_IC的時(shí)候,,返回E_NOINTERFACE。
       聚合B組件的所有接口的方式稱為盲聚合,。盲聚合的缺點(diǎn)是:如果B組件實(shí)現(xiàn)了IPersist接口,,客戶調(diào)用IPersist::GetClassID方法時(shí),獲得的是CLSID_CB,,這就暴露了內(nèi)部的B組件,;還有就是B組件實(shí)現(xiàn)的接口不了解A組件的狀態(tài),如果B組件實(shí)現(xiàn)了ISave接口,,客戶調(diào)用了它,,卻不能正確完成保存組件狀態(tài)的功能。所以,,一般我們要避免使用盲聚合,,而要有選擇的聚合,。
       外部組件還有一個(gè)重要的任務(wù)就是控制內(nèi)部組件的生命周期。因?yàn)閮?nèi)部組件擁有外部組件的IUnknown指針,,這時(shí)候當(dāng)調(diào)用內(nèi)部組件的AddRef/Release,,改變的是外部組件的引用計(jì)數(shù)。所以如果需要減少引用計(jì)數(shù),,應(yīng)該調(diào)用pUnknowOuter(pUnknowOuter其實(shí)就是this指針的強(qiáng)制轉(zhuǎn)換)->Release( ),。CA類析構(gòu)時(shí),要采用以下特殊的做法保證不會(huì)被過(guò)早的析構(gòu)和多次釋放,。
       m_cRef=1;
       IUnknown* pUnknownOuter = this;
       pUnknownOuter->AddRef( );
       m_pIB->Release( );
需要注意的是CA的析構(gòu)函數(shù)是在A組件自身的引用計(jì)數(shù)為0時(shí)才會(huì)被調(diào)用,,如果沒(méi)有m_cRef=1這行代碼,m_pIB->Release( )會(huì)導(dǎo)致析構(gòu)函數(shù)再次被調(diào)用,。
`      最后,,析溝函數(shù)將釋放組件B。通過(guò)調(diào)用B的INondelegationUnknown::Release( )方法,。
代碼如下:
       if(m_pUnknownInner!=NULL)
{
       m_pUnknownInner->Release ( );
}
 
以上總結(jié)主要來(lái)自于<<COM技術(shù)內(nèi)幕>>,。但是在ATL中,一切都被隱藏的很多,。
ATL7對(duì)內(nèi)部組件的支持
        使用ATL7.1創(chuàng)建ATL簡(jiǎn)單對(duì)象(傳統(tǒng)COM對(duì)象時(shí))向?qū)е锌梢赃x擇是否支持聚合,,默認(rèn)是支持的。我們現(xiàn)在開(kāi)始創(chuàng)建內(nèi)部組件,。
 
 
   
 
 
      
 
      
 
      
 
       按照上圖步驟創(chuàng)建組件B,,并且實(shí)現(xiàn)了IB接口。現(xiàn)在我們?yōu)镮B增加方法F( ),。F()方法很簡(jiǎn)單,,只是彈出一個(gè)消息框。
       STDMETHODIMP CB::F(void)
{
          MessageBox(NULL,"OK","OK",MB_OK);
          return S_OK;
}
 
DECLARE_CLASSFACTORY()-------------
在ATL中,,類廠是由CComCoClass類的宏DECLARE_CLASSFACTORY()實(shí)現(xiàn)的,。該宏的定義為:#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory)
CComClassFactory實(shí)現(xiàn)了IClassFactory接口。最主要的就是實(shí)現(xiàn)了創(chuàng)建組件的函數(shù),。
// IClassFactory
     STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
     {
         ATLASSERT(m_pfnCreateInstance != NULL);
         HRESULT hRes = E_POINTER;
         if (ppvObj != NULL)
         {
              *ppvObj = NULL;
              // can't ask for anything other than IUnknown when aggregating
              if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
              {
                   ATLTRACE(atlTraceCOM, 0, _T("CComClassFactory: asked for non IUnknown interface while creating an aggregated object"));
                   hRes = CLASS_E_NOAGGREGATION;
              }
              else
                   hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
         }
         return hRes;
}
m_pfnCreateInstance是創(chuàng)建COM對(duì)象的函數(shù)的指針,。創(chuàng)建CB類的函數(shù)指針是被保存到對(duì)象映射表中的用于創(chuàng)建CB類的函數(shù)指針。對(duì)象映射表是一個(gè)_ATL_OBJMAP_ENTRY結(jié)構(gòu)數(shù)組,,該結(jié)構(gòu)數(shù)組還保存了創(chuàng)建類廠的函數(shù)指針。該結(jié)構(gòu)數(shù)組的初始化是通過(guò)B.h中的宏OBJECT_ENTRY_AUTO(__uuidof(B), CB)來(lái)完成的,。該宏取代了ATL3中的BEGIN_OBJECT_MAP()/OBJECT_ENTRY()/END_OBJECT_MAP()宏,。
 
宏DECLARE_CLASSFACTORY_EX定義為#define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator< ATL::CComObjectCached< cf > > _ClassFactoryCreatorClass;
CComObjectCached類派生自CComClassFactory,該類的作用是使得組件類的生命周期和服務(wù)器生命周期相同,,所以適用于進(jìn)程內(nèi)組件,。CComCreator類只提供了一個(gè)CreateInstance函數(shù),,用來(lái)幫助我們創(chuàng)建一個(gè)組件類,這里就幫助我們創(chuàng)建類廠,。
所以宏DECLARE_CLASSFACTORY()的功能就是:
1) 通過(guò)從CComClassFactory類派生實(shí)現(xiàn)了IClassFactory接口
2) 使用CComObjectCached類指定了類廠對(duì)象的生命周期和服務(wù)器生命周期相同
3) 定義了_ClassFactoryCreatorClass類型,,該類型的CreateInstance函數(shù)可以創(chuàng)建類廠對(duì)象,而且是采用多步構(gòu)造的方式,防止了構(gòu)造期間的意外析構(gòu),。
 
 
DECLARE_AGGREGATABLE(T)---------------------
該宏的定義如下:
#define DECLARE_AGGREGATABLE(x) public:/
     typedef ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< x > >, ATL::CComCreator< ATL::CComAggObject< x > > > _CreatorClass;
    _CreatorClass類型的CreateInstance函數(shù)即可以創(chuàng)建獨(dú)立組件,,也可以創(chuàng)建聚合組件,取決于傳入的pOuterIUnknown參數(shù)是否為NULL,。
值得一提的是CComAggObject類通過(guò)兩次從CComObjectRootEx繼承,,實(shí)現(xiàn)了內(nèi)部組件的兩個(gè)IUnknown接口,一個(gè)是IUnknown*,,一個(gè)是INondelegationUnknown*(其實(shí)ATL中不使用INondelegationUnknown名字,,但是作用一樣和INondelegationUnknown一樣)。
如果我們?cè)谙驅(qū)е羞x擇不支持聚合,,向?qū)?huì)在我們的CB類內(nèi)加上宏#define DECLARE_NOT_AGGREGATABLE(x)
#define DECLARE_NOT_AGGREGATABLE(x) public:/
     typedef ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< x > >, ATL::CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass;
這樣_CreatorClass類型的CreateInstance函數(shù)當(dāng)接收到不為NULL的pOuterIUnknown參數(shù)時(shí)會(huì)失敗,。
 
所以,當(dāng)我們有了ATL的支持后,,我們創(chuàng)建內(nèi)部組件的一切工作只是在向?qū)е羞x擇支持聚合,,輕松愜意!
ATL7對(duì)外部組件的支持
       現(xiàn)在我們另創(chuàng)建一個(gè)名為OuterComponent的ATL項(xiàng)目,,創(chuàng)建A組件并實(shí)現(xiàn)IA接口,。創(chuàng)建步驟就不再敘述。A組件是否支持聚合無(wú)關(guān)緊要,,我們這里就默認(rèn)支持吧,。在OuterComponent項(xiàng)目中導(dǎo)入InnerComponent.dll,然后將CComPtr<IUnknown> m_pIB作為CA的成員變量,。請(qǐng)參考下面的代碼的注釋
// A.h : CA 的聲明
 
#pragma once
#include "resource.h"       // 主符號(hào)
 
#include "OuterComponent.h"
 
 
// CA
 
class ATL_NO_VTABLE CA :
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CA, &CLSID_A>,
     public IDispatchImpl<IA, &IID_IA, &LIBID_OuterComponentLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
     CA()
     {
     }
 
DECLARE_REGISTRY_RESOURCEID(IDR_A)
 
BEGIN_COM_MAP(CA)
     COM_INTERFACE_ENTRY(IA)
     COM_INTERFACE_ENTRY(IDispatch)
     COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IB,m_spunkIB.p,CLSID_B)//自動(dòng)聚合IB接口,,有了該宏就不需要在FinalConstruct函數(shù)中創(chuàng)建B對(duì)象了
END_COM_MAP()
 
     DECLARE_GET_CONTROLLING_UNKNOWN()//創(chuàng)建虛函數(shù)IUnknown* GetControllingUnknown(),CComContainedObject類會(huì)繼承并實(shí)現(xiàn)該函數(shù),,然后將返回最外層組件的IUnknown指針
     DECLARE_PROTECT_FINAL_CONSTRUCT()
     HRESULT FinalConstruct()
     {
         return S_OK;
     }
    
     void FinalRelease()
     {
         m_spunkIB.Release();//避免兩次析構(gòu)內(nèi)部組件
     }
private:
     CComPtr<IUnknown> m_spunkIB;//保存被聚合的內(nèi)部組件的IUnknown接口
};
OBJECT_ENTRY_AUTO(__uuidof(A), CA)
 
   
    現(xiàn)在我們來(lái)用一下,,創(chuàng)建一個(gè)客戶程序,它導(dǎo)入A和B的組件類型庫(kù),,然后創(chuàng)建A組件,,然后查詢IB接口并調(diào)用IB::F()方法。
 
// Client.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn),。
//
 
#include "stdafx.h"
#import "../../OuterComponent/OuterComponent/debug/OuterComponent.tlb" no_namespace named_guids raw_interfaces_only
#import "../../InnerComponent/InnerComponent/debug/InnerComponent.tlb" no_namespace named_guids raw_interfaces_only
 
 
int _tmain(int argc, _TCHAR* argv[])
{
     ::CoInitialize(NULL);
     CComPtr<IA> pA;
     HRESULT hr=pA.CoCreateInstance(CLSID_A);
     CComPtr<IB> pB;
     hr=pA->QueryInterface(&pB);
     pB->F();
     pB.Release();
     pA.Release();
     ::CoUninitialize();
     return 0;
}
 
一切成功,。ATL大大簡(jiǎn)化了我們對(duì)于聚合的使用。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多