乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      COM聚合

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

             組件B
      IB
       

       
       
       

      客戶直接使用
       
       

      IA
       


      客戶直接使用
       
      內(nèi)部組件的實現(xiàn)
             客戶程序只知道A組件而不知道B組件,并且認為A組件實現(xiàn)了IA和IB接口。因此,當客戶創(chuàng)建了A組件(通過CoCreateInstance函數(shù)),獲取到IUnknown接口時,應該獲得的是A組件實現(xiàn)的IUnknown接口。      問題在于B組件有自己的IUnknown的接口實現(xiàn),如果B組件還是采用一般的方法實現(xiàn)IUnknown接口的話,當客戶調(diào)用IB::QueryInterface函數(shù),就不會得到IA接口,這當然是不允許的。所以一個支持聚合的組件,它的IUnknown實現(xiàn)必然要有別于普通組件。
             B組件應該擁有一個成員變量IUnknown* m_pUnknownOuter;該指針可以指向A組件的IUnknown接口。當客戶調(diào)用IB::QueryInterface請求時,如果IB接口已經(jīng)被聚合了,就調(diào)用m_pUnknownOuter->QueryInterface方法,這實際上就是調(diào)用了A組件的QueryInterface方法。那么,m_pUnknownOuter指針是什么時候被初始化的呢?CoCreateInstance函數(shù)和IClassFactory::CreateInstance方法都接受一個IUnknown參數(shù)。如果A組件內(nèi)部想聚合B組件的IB接口,他就會將自己的IUnknown指針傳遞進去,如果A組件并不想聚合B組件,那么簡單的傳遞一個NULL就行了。
             B組件可以根據(jù)m_pUnknownOuter是否為NULL,來判斷是否被聚合。
             B組件實現(xiàn)的具體步驟如下:
      1) 聲明一個INondelegationUnknown接口,該接口擁有IUnknown接口一樣的純虛函數(shù),當然函數(shù)名前面均加上Nondelegation前綴。
      2) CB類(假設CB類為組件類)繼承并實現(xiàn)INondelegationUnknown接口,實現(xiàn)代碼和普通組件的IUnknown一樣,這用于非聚合的情況下。
      3) CB類的構造函數(shù)接受IUnknown指針,如果傳遞進來的是NULL,說明B組件并不被用作聚合。m_pUnknownOuter變量就指向B組件自身的IUnknown接口。如果傳遞進來的不是NULL,說明被用作聚合。m_pUnknownOuter變量值等于參數(shù)值,指向外部組件的IUnknown接口指針。
      4) CB類也要繼承并實現(xiàn)IUnknown接口。這個接口的QueryInterface函數(shù)實現(xiàn)只是調(diào)用m_pUnknownOuter->QueryInterfac方法。m_pUnknownOuter究竟代表什么取決于創(chuàng)建組件時是否傳遞了外部組件的IUnknown指針。
      5) B組件的類廠的CreateInstance方法內(nèi)部創(chuàng)建CB類時,將IUnknown* pUnkownOuter參數(shù)傳遞給構造函數(shù)。創(chuàng)建成功后,調(diào)用B組件自身的(而不是外部的)NondelegationQueryInterface方法,將自身的INondelegationUnknown傳遞出去給組件A。
       
       
      外部組件的實現(xiàn)
             外部組件A要創(chuàng)建組件B的實例,并保存B組件的INondelegationUnknown接口指針。該接口指針是通過上一節(jié)5)由B的類廠返回的。m_pUnknownInner變量保存了B接口的INondelegationUnknown接口指針。外部組件調(diào)用CoCreateInstance函數(shù)創(chuàng)建聚合組件時,iid必須等于IID_IUnknown。
             外部組件要修改自己的QueryInterface,當客戶程序請求IB接口的時候,將調(diào)用m_pUnknownInner->QueryInterface方法。
             外部組件可以通過QueryInterface的代碼來控制是否聚合B組件所有的接口。如果不想聚合B組件實現(xiàn)的IC接口??梢栽谳斎?yún)?shù)iid等于IID_IC的時候,返回E_NOINTERFACE。
             聚合B組件的所有接口的方式稱為盲聚合。盲聚合的缺點是:如果B組件實現(xiàn)了IPersist接口,客戶調(diào)用IPersist::GetClassID方法時,獲得的是CLSID_CB,這就暴露了內(nèi)部的B組件;還有就是B組件實現(xiàn)的接口不了解A組件的狀態(tài),如果B組件實現(xiàn)了ISave接口,客戶調(diào)用了它,卻不能正確完成保存組件狀態(tài)的功能。所以,一般我們要避免使用盲聚合,而要有選擇的聚合。
             外部組件還有一個重要的任務就是控制內(nèi)部組件的生命周期。因為內(nèi)部組件擁有外部組件的IUnknown指針,這時候當調(diào)用內(nèi)部組件的AddRef/Release,改變的是外部組件的引用計數(shù)。所以如果需要減少引用計數(shù),應該調(diào)用pUnknowOuter(pUnknowOuter其實就是this指針的強制轉換)->Release( )。CA類析構時,要采用以下特殊的做法保證不會被過早的析構和多次釋放。
             m_cRef=1;
             IUnknown* pUnknownOuter = this;
             pUnknownOuter->AddRef( );
             m_pIB->Release( );
      需要注意的是CA的析構函數(shù)是在A組件自身的引用計數(shù)為0時才會被調(diào)用,如果沒有m_cRef=1這行代碼,m_pIB->Release( )會導致析構函數(shù)再次被調(diào)用。
      `      最后,析溝函數(shù)將釋放組件B。通過調(diào)用B的INondelegationUnknown::Release( )方法。
      代碼如下:
             if(m_pUnknownInner!=NULL)
      {
             m_pUnknownInner->Release ( );
      }
       
      以上總結主要來自于<<COM技術內(nèi)幕>>。但是在ATL中,一切都被隱藏的很多。
      ATL7對內(nèi)部組件的支持
              使用ATL7.1創(chuàng)建ATL簡單對象(傳統(tǒng)COM對象時)向導中可以選擇是否支持聚合,默認是支持的。我們現(xiàn)在開始創(chuàng)建內(nèi)部組件。
       
       
         
       
       
            
       
            
       
            
       
             按照上圖步驟創(chuàng)建組件B,并且實現(xiàn)了IB接口。現(xiàn)在我們?yōu)镮B增加方法F( )。F()方法很簡單,只是彈出一個消息框。
             STDMETHODIMP CB::F(void)
      {
                MessageBox(NULL,"OK","OK",MB_OK);
                return S_OK;
      }
       
      DECLARE_CLASSFACTORY()-------------
      在ATL中,類廠是由CComCoClass類的宏DECLARE_CLASSFACTORY()實現(xiàn)的。該宏的定義為:#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory)
      CComClassFactory實現(xiàn)了IClassFactory接口。最主要的就是實現(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對象的函數(shù)的指針。創(chuàng)建CB類的函數(shù)指針是被保存到對象映射表中的用于創(chuàng)建CB類的函數(shù)指針。對象映射表是一個_ATL_OBJMAP_ENTRY結構數(shù)組,該結構數(shù)組還保存了創(chuàng)建類廠的函數(shù)指針。該結構數(shù)組的初始化是通過B.h中的宏OBJECT_ENTRY_AUTO(__uuidof(B), CB)來完成的。該宏取代了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,該類的作用是使得組件類的生命周期和服務器生命周期相同,所以適用于進程內(nèi)組件。CComCreator類只提供了一個CreateInstance函數(shù),用來幫助我們創(chuàng)建一個組件類,這里就幫助我們創(chuàng)建類廠。
      所以宏DECLARE_CLASSFACTORY()的功能就是:
      1) 通過從CComClassFactory類派生實現(xiàn)了IClassFactory接口
      2) 使用CComObjectCached類指定了類廠對象的生命周期和服務器生命周期相同
      3) 定義了_ClassFactoryCreatorClass類型,該類型的CreateInstance函數(shù)可以創(chuàng)建類廠對象,而且是采用多步構造的方式,防止了構造期間的意外析構。
       
       
      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)建獨立組件,也可以創(chuàng)建聚合組件,取決于傳入的pOuterIUnknown參數(shù)是否為NULL。
      值得一提的是CComAggObject類通過兩次從CComObjectRootEx繼承,實現(xiàn)了內(nèi)部組件的兩個IUnknown接口,一個是IUnknown*,一個是INondelegationUnknown*(其實ATL中不使用INondelegationUnknown名字,但是作用一樣和INondelegationUnknown一樣)。
      如果我們在向導中選擇不支持聚合,向導將會在我們的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ù)當接收到不為NULL的pOuterIUnknown參數(shù)時會失敗。
       
      所以,當我們有了ATL的支持后,我們創(chuàng)建內(nèi)部組件的一切工作只是在向導中選擇支持聚合,輕松愜意!
      ATL7對外部組件的支持
             現(xiàn)在我們另創(chuàng)建一個名為OuterComponent的ATL項目,創(chuàng)建A組件并實現(xiàn)IA接口。創(chuàng)建步驟就不再敘述。A組件是否支持聚合無關緊要,我們這里就默認支持吧。在OuterComponent項目中導入InnerComponent.dll,然后將CComPtr<IUnknown> m_pIB作為CA的成員變量。請參考下面的代碼的注釋
      // A.h : CA 的聲明
       
      #pragma once
      #include "resource.h"       // 主符號
       
      #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)//自動聚合IB接口,有了該宏就不需要在FinalConstruct函數(shù)中創(chuàng)建B對象了
      END_COM_MAP()
       
           DECLARE_GET_CONTROLLING_UNKNOWN()//創(chuàng)建虛函數(shù)IUnknown* GetControllingUnknown(),CComContainedObject類會繼承并實現(xiàn)該函數(shù),然后將返回最外層組件的IUnknown指針
           DECLARE_PROTECT_FINAL_CONSTRUCT()
           HRESULT FinalConstruct()
           {
               return S_OK;
           }
          
           void FinalRelease()
           {
               m_spunkIB.Release();//避免兩次析構內(nèi)部組件
           }
      private:
           CComPtr<IUnknown> m_spunkIB;//保存被聚合的內(nèi)部組件的IUnknown接口
      };
      OBJECT_ENTRY_AUTO(__uuidof(A), CA)
       
         
          現(xiàn)在我們來用一下,創(chuàng)建一個客戶程序,它導入A和B的組件類型庫,然后創(chuàng)建A組件,然后查詢IB接口并調(diào)用IB::F()方法。
       
      // Client.cpp : 定義控制臺應用程序的入口點。
      //
       
      #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大大簡化了我們對于聚合的使用。

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多