1. <tt id="5hhch"><source id="5hhch"></source></tt>
    1. <xmp id="5hhch"></xmp>

  2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

    <rp id="5hhch"></rp>
        <dfn id="5hhch"></dfn>

      1. C++類的動(dòng)態(tài)組件化技術(shù)

        • 相關(guān)推薦

        C++類的動(dòng)態(tài)組件化技術(shù)

          論文關(guān)鍵詞:COM組件 接口 生命周期 C++類 ATL組件類 C++基類 ATL模板基類 繼承

          論文摘要:在組件化編程的時(shí)代,如何復(fù)用累積的大量沒有組件特性的C++類?本文從工程的角度對(duì)這一問題進(jìn)行探討,利用現(xiàn)有組件技術(shù),提出了一套將C++類平滑過渡到COM組件的完整解決方案。

          1. 問題的提出

          自從Microsoft公布了COM(Component Object Model,組件對(duì)象模型,簡(jiǎn)稱COM)技術(shù)以后,Windows平臺(tái)上的開發(fā)模式發(fā)生了巨大的變化,以COM為基礎(chǔ)的一系列組件技術(shù)將Windows編程帶入了組件化時(shí)代,傳統(tǒng)的面向?qū)ο蟮能浖_發(fā)方法已經(jīng)逐漸被面向組件的方法所取代。

          COM標(biāo)準(zhǔn)建立在二進(jìn)制可執(zhí)行代碼級(jí)的基礎(chǔ)上,不論何種工具、開發(fā)的組件,只要符合COM規(guī)范,就可復(fù)用于VC、VB、Delphi、BC等各種開發(fā)中。COM的語言無關(guān)性將軟件復(fù)用的層次從源代碼級(jí)推進(jìn)到了二進(jìn)制級(jí),復(fù)用更方便,也更安全。

          然而,COM技術(shù)帶來全新的軟件設(shè)計(jì)和開發(fā)模式的同時(shí),也帶來了新的問題。

          許多軟件公司在開發(fā)自己的軟件產(chǎn)品過程中,都累積了大量C++類,這些代碼設(shè)計(jì)精良,功能完備,以面向?qū)ο蟮臉?biāo)準(zhǔn)來無可挑剔。然而,這些代碼不支持COM,將無法在COM時(shí)代繼續(xù)被復(fù)用。如果它們?cè)谲浖M件化的趨勢(shì)中被淘汰,那對(duì)軟件公司和開發(fā)人員來說都是極大的損失。

          COM專家Don Box曾說過,“COM is a super C++”。這給了我們一個(gè)啟示,是否可以實(shí)現(xiàn)一種技術(shù),能夠動(dòng)態(tài)的為普通C++類加上一層COM的封裝呢?這樣,既可以保持這些代碼自身的完整和特性,使它們能繼續(xù)應(yīng)用于原來的系統(tǒng),也可以在需要作為組件使用的時(shí)候,把它們動(dòng)態(tài)轉(zhuǎn)變成組件,復(fù)用于新系統(tǒng)。

          一個(gè)自然而然的想法是,為每一個(gè)C++類開發(fā)一個(gè)只暴露一個(gè)接口的COM組件,將原C++類的每個(gè)public方法都對(duì)應(yīng)于該接口的一個(gè)方法,接口方法的實(shí)現(xiàn)可以簡(jiǎn)單的調(diào)用相對(duì)應(yīng)的C++類方法即可。這樣,程序由原有的C++類控制,但COM層的封裝則由組件提供。基本思路如下圖所示:

          

          本文就這一技術(shù)展開討論,最終提供一套由普通C++類平滑過渡到COM組件的完整解決方案。我們選用ATL(Active Template Library,活動(dòng)模板庫(kù),簡(jiǎn)稱ATL)作為COM組件的開發(fā)工具,開發(fā)環(huán)境為Visual Studio 6.0。如沒有特殊說明,下文中的“C++類”指沒有組件特性C++類,“C++對(duì)象”指C++類的實(shí)例;“ATL組件類”指用于包裝的ATL類,“ATL對(duì)象”指ATL組件類的實(shí)例。

          2. 用ATL包裝C++類

          按上述思路將C++對(duì)象動(dòng)態(tài)組件化后,所得的組件實(shí)際上由兩部分組成:ATL組件對(duì)象和綁定的C++對(duì)象。兩者的生命周期互相牽制,但要保持一致。生命周期的是C++類動(dòng)態(tài)組件化的首要難點(diǎn)。

          C++類分為兩種,一種是簡(jiǎn)單的C++類,一種是集合型的C++類。集合型的C++對(duì)象管理一組C++對(duì)象,負(fù)責(zé)其創(chuàng)建和刪除,維護(hù)它們的生命周期。下面,分別就簡(jiǎn)單C++類和集合型C++類的組件化技術(shù)進(jìn)行說明,展示解決方案的核心技術(shù)。

          2.1. 簡(jiǎn)單C++類的組件化

          為使ATL組件類可以自由調(diào)用C++類的方法,需要:

          l 為ATL組件類安插一個(gè)指針成員變量,指向C++類

          l 提供ATL對(duì)象和C++對(duì)象的綁定機(jī)制

          我們可以在ATL組件類初始化時(shí)創(chuàng)建一個(gè)C++類,用成員變量m_pCPPObj記錄,在析構(gòu)時(shí)刪除,從而實(shí)現(xiàn)ATL組件類和C++類的天然綁定。但出于靈活性考慮,使得ATL組件對(duì)象可以綁定任意C++類的對(duì)象,我們?yōu)锳TL組件類添加一個(gè)綁定函數(shù)Link2CPPObj(CImplement* pObj)。

          在ATL組件類的構(gòu)造函數(shù)內(nèi),創(chuàng)建一個(gè)C++對(duì)象,用m_pCPPObj記錄。

          如果調(diào)用了Link2CPPObj,則將m_pCPPObj指向的對(duì)象刪除,改用傳入的C++對(duì)象。

          在ATL組件類的的析構(gòu)函數(shù)內(nèi),刪除其綁定的C++對(duì)象。由構(gòu)造函數(shù)和Link2CPPObj函數(shù)的定義可知,m_pCPPObj指針總是有意義的。

          簡(jiǎn)單C++類組件化的思想如下圖所示:

          

          2.2. 集合型C++類的組件化

          集合型C++類的情況有所不同。

          集合型C++類以數(shù)組(array)、列表(list)、映射表(map)的形式其它C++對(duì)象。集合對(duì)象和它管理的元素對(duì)象都被包裝成組件后,集合型ATL對(duì)象可能調(diào)用一個(gè)“Destroy”方法,期望刪除某一個(gè)元素ATL對(duì)象;這一操作的實(shí)質(zhì)卻是,集合型C++對(duì)象的“Destroy”方法被調(diào)用,將元素C++對(duì)象刪除了,而元素ATL對(duì)象卻不知道。這一操作的結(jié)果導(dǎo)致了元素的ATL對(duì)象存在,而其綁定的C++對(duì)象卻被刪除的情況,兩者的生命周期出現(xiàn)了不一致。

          為了解決這個(gè)問題,我們需要在C++對(duì)象被刪除時(shí),能將ATL對(duì)象同時(shí)刪除;而在ATL對(duì)象的引用計(jì)數(shù)為0需要?jiǎng)h除自身時(shí),也能把C++對(duì)象刪除。可行的解決方案是:

          l 在C++類中保存一個(gè)接口指針,指向綁定在一起的ATL對(duì)象;為該接口指針賦值的最佳地點(diǎn)顯然是提供綁定機(jī)制的Link2CPPObj函數(shù)內(nèi)部,為此,還需要給Link2CPPObj添加一個(gè)IUnknown*參數(shù)

          l 在C++類的析構(gòu)函數(shù)中,判斷該接口指針是否為空,如果不為空,則Release對(duì)接口的引用,引發(fā)ATL對(duì)象自身的析構(gòu)

          現(xiàn)在,技術(shù)方案如下圖所示:

          

          2.3. 內(nèi)部創(chuàng)建的組件和外部創(chuàng)建的組件

          集合型C++類組件化后仍然是集合型ATL組件,它可以創(chuàng)建、刪除自己管理的組件。這樣,組件的創(chuàng)建就可能有兩種情況:

          l 由客戶直接創(chuàng)建

          l 由客戶調(diào)用集合型組件的接口方法間接創(chuàng)建

          創(chuàng)建方式的不同導(dǎo)致了組件生命周期的復(fù)雜性。一般說來,組件的創(chuàng)建者負(fù)責(zé)維護(hù)組件的生命周期。上述兩種情況下,分別由客戶和集合型組件維護(hù)被創(chuàng)建組件的生命周期。然而,另有一種情況是,客戶創(chuàng)建了一個(gè)組件,然后送交一個(gè)集合型組件管理,現(xiàn)在維護(hù)組件生命周期的責(zé)任就由客戶轉(zhuǎn)交給了集合型組件。

          我們的解決方案必須提供這樣的健壯性和靈活性,以維護(hù)各種情況下組件的生命周期。我們?yōu)锳TL組件類添加一個(gè)BOO成員m_bInnerManage,作為組件的維護(hù)標(biāo)識(shí)。內(nèi)部維護(hù)意味著組件的生命周期由其它組件(集合型組件)維護(hù);外部維護(hù)則是由客戶維護(hù)。

          

          缺省情況下,組件是外部創(chuàng)建并維護(hù)的,在組件的構(gòu)造函數(shù)內(nèi)設(shè)置外部維護(hù)標(biāo)識(shí)。集合型組件創(chuàng)建元素時(shí),需要為元素分別創(chuàng)建一個(gè)C++對(duì)象和一個(gè)ATL對(duì)象,然后調(diào)用ATL對(duì)象的Link2CPPObj函數(shù)將兩者綁定在一起,在Link2CPPObj函數(shù)內(nèi)修改維護(hù)標(biāo)識(shí)。對(duì)于第三種情況,可以在外部創(chuàng)建組件由客戶轉(zhuǎn)交給集合型組件時(shí),在集合型組件相應(yīng)方法內(nèi)重新設(shè)置維護(hù)標(biāo)識(shí)。

          2.4. C++基類

          為了對(duì)現(xiàn)有C++類的改動(dòng)最小,我們?cè)O(shè)計(jì)一個(gè)基類封裝需要為C++類添加的功能。所有需要?jiǎng)討B(tài)組件化的C++類都必須從這個(gè)基類派生,以保證動(dòng)態(tài)組件化中C++對(duì)象與ATL對(duì)象生命周期的一致。如下圖示:

          

          實(shí)現(xiàn)代碼如下所示:

        class CCPP2ATLObjBase

        {

               CCPP2ATLObjBase ();

        public:

               // IUnknown指針,反指向封裝該CPP類的接口

               IUnknown*    m_pAssociATLUnk;

        protected:

               virtual ~ CCPP2ATLObjBase ();

        };

        CCPP2ATLObjBase::CCPP2ATLObjBase()

        {

               // 將IUnknown指針初始化為0

               m_pAssociATLUnk = NULL;

        }

        CCPP2ATLObjBase::~CCPP2ATLObjBase()

        {

               // CPP類的對(duì)象析構(gòu)時(shí),Release對(duì)接口的引用

               if (m_pAssociATLUnk)

                      m_pAssociATLUnk->Release();

        }

        然后,修改現(xiàn)有各個(gè)C++類,使之從CCPP2ATLObjBase派生,如下面代碼片斷所示:

        class CImplement : public CCPP2ATLObjBase

        {

               ……

        };

          必須指出的是,在CCPP2ATLObjBase基類中,我們?cè)O(shè)置的m_pAssociATLUnk變量存在和現(xiàn)有C++類成員命名沖突的問題。但是,考慮到原C++類并沒有組件特性,也應(yīng)該不會(huì)有“IUnknown”型指針,因此,只要各個(gè)類的變量命名都按照規(guī)范的命名法,出現(xiàn)這種名字沖突的可能性是極小的。

          2.5. ATL模板基類

          通過以上分析,我們發(fā)現(xiàn),所有的ATL組件類都需要實(shí)現(xiàn)一些相同的功能:

          l 保留一個(gè)指向其綁定C++對(duì)象的指針

          l 提供一個(gè)Link2CPPObj函數(shù)

          l 在構(gòu)造函數(shù)中創(chuàng)建一個(gè)綁定C++類的對(duì)象

          為了減化編碼,我們定義一個(gè)帶參數(shù)的模板基類,實(shí)現(xiàn)上述功能,模板參數(shù)就是綁定的C++類。然后,所有的ATL組件類都從模板基類中派生。現(xiàn)在的技術(shù)方案如下圖所示:

          

        實(shí)現(xiàn)代碼如下所示:

        template <class T>

        class CCPP2ATLTemplateBase :

        {

        protected:

               // C++類指針

               T*          m_pCPPObj;

               // 標(biāo)識(shí)繼承該模板的ATL對(duì)象是否由內(nèi)部維護(hù)

               BOOL     m_bInnerManage;

        public:

               /**********************************************************

                 模板的構(gòu)造函數(shù),實(shí)現(xiàn)如下功能:

                 1、new一個(gè)C++實(shí)現(xiàn)類對(duì)象

                 2、缺省情況下,ATL對(duì)象由外部維護(hù),將內(nèi)部維護(hù)標(biāo)識(shí)設(shè)為FALSE

                 3、將C++類中對(duì)ATL接口的反指指針設(shè)置為空

               **********************************************************/

               CAtlCPP2ATLTemplateBase()

               {

                      m_pCPPObj = new T;

                      m_bInnerManage = FALSE;

                      m_pCPPObj->m_pAssociATLUnk = NULL;

               }

               /**********************************************************

                 析構(gòu)ATL對(duì)象時(shí),如果該ATL對(duì)象是由外部創(chuàng)建的,

                 則顯式的刪除C++對(duì)象

                 如果ATL對(duì)象由內(nèi)部維護(hù),那么什么事都不用做

               **********************************************************/

               virtual ~CAtlCPP2ATLTemplateBase()

               {

                      if (!m_bInnerManage) {

                             if (m_pCPPObj)

                                    delete m_pCPPObj;

                      }

               }

               /**********************************************************

                 Link2CPPObj函數(shù),負(fù)責(zé)綁定C++對(duì)象和ATL接口

                 1、刪除構(gòu)造函數(shù)中new的C++對(duì)象,而使用外部傳入的C++對(duì)象

                 2、將ATL對(duì)象的內(nèi)部維護(hù)標(biāo)識(shí)設(shè)為TRUE

                 3、設(shè)置C++基類中的接口指針成員

                 4、因?yàn)锳TL接口傳送給外部使用,需要增加引用計(jì)數(shù)

               **********************************************************/

               virtual void Link2CPPObj(T* pObj, IUnknown* pUnk)

               {

                      ASSERT(pObj != NULL);

                      ASSERT(pUnk != NULL);

                      if (m_pCPPObj)

                             delete m_pCPPObj;

                      m_pCPPObj = pObj;

                      m_bInnerManage = TRUE;

                      m_pCPPObj->m_pAssociATLUnk = pUnk;

                      m_pCPPObj->m_pAssociATLUnk->AddRef();

               }

        };

        然后,每個(gè)ATL類都從該模板類派生,如下代碼片斷所示:

        class ATL_NO_VTABLE CATLXX :

               ……,

               // 添加ATL模板基類

               public CCPP2ATLTemplateBase<CImplementXX>

        {

               ……

        }

          3.   C++參數(shù)類型的自動(dòng)化包裝

          在本文的技術(shù)方案中,C++類的public方法與ATL組件接口中的方法一一對(duì)應(yīng);相應(yīng)的,C++類中方法的參數(shù)類型也要轉(zhuǎn)換為COM規(guī)范所允許的數(shù)據(jù)類型。

          在基于COM的自動(dòng)化(Automation)技術(shù)中,Microsoft提供了一套自動(dòng)化兼容的數(shù)據(jù)類型VARIANT,定義如下:

          typedef struct FARSTRUCT tagVARIANT VARIANT;

          typedef struct FARSTRUCT tagVARIANT VARIANTARG;

          typedef struct tagVARIANT {

               VARTYPE                        vt;

               unsigned short                  wReserved1;

               unsigned short                  wReserved2;

               unsigned short                  wReserved3;

               union {

                      Byte                         bVal;                               // VT_UI1.

                      Short                        iVal;                                // VT_I2.

                      long                          lVal;                                // VT_I4.

                      float                         fltVal;                              // VT_R4.

                      double                      dblVal;                            // VT_R8.

                      VARIANT_BOOL      boolVal;                           // VT_BOOL.

                      SCODE                    scode;                            // VT_ERROR.

                      CY                           cyVal;                             // VT_CY.

                      DATE                       date;                               // VT_DATE.

                      BSTR                       bstrVal;                           // VT_BSTR.

                      DECIMAL                FAR* pdecVal;                 // VT_BYREF|VT_DECIMAL.

                      IUnknown                 FAR* punkVal;                 // VT_UNKNOWN.

                      IDispatch                  FAR* pdispVal;                // VT_DISPATCH.

                      SAFEARRAY            FAR* parray;                   // VT_ARRAY|*.

                      Byte                         FAR* pbVal;                    // VT_BYREF|VT_UI1.

                      short                        FAR* piVal;                     // VT_BYREF|VT_I2.

                      long                          FAR* plVal;                     // VT_BYREF|VT_I4.

                      float                         FAR* pfltVal;                   // VT_BYREF|VT_R4.

                      double                      FAR* pdblVal;                  // VT_BYREF|VT_R8.

                      VARIANT_BOOL      FAR* pboolVal;                // VT_BYREF|VT_BOOL.

                      SCODE                    FAR* pscode;                  // VT_BYREF|VT_ERROR.

                      CY                           FAR* pcyVal;                  // VT_BYREF|VT_CY.

                      DATE                       FAR* pdate;                    // VT_BYREF|VT_DATE.

                      BSTR                       FAR* pbstrVal;                // VT_BYREF|VT_BSTR.

                      IUnknown                 FAR* FAR* ppunkVal;      // VT_BYREF|VT_UNKNOWN.

                      IDispatch                  FAR* FAR* ppdispVal;     // VT_BYREF|VT_DISPATCH.

                      SAFEARRAY            FAR* FAR* pparray         // VT_ARRAY|*.

                      VARIANT                 FAR* pvarVal;                 // VT_BYREF|VT_VARIANT.

                      void                          FAR* byref;                    // Generic ByRef.

                      char                         cVal;                               // VT_I1.

                      unsigned short           uiVal;                              // VT_UI2.

                      unsigned long            ulVal;                              // VT_UI4.

                      int                            intVal;                             // VT_INT.

                      unsigned int               uintVal;                           // VT_UINT.

                      char FAR *               pcVal;                             // VT_BYREF|VT_I1.

                      unsigned short FAR * puiVal;                            // VT_BYREF|VT_UI2.

                      unsigned long FAR *  pulVal;                            // VT_BYREF|VT_UI4.

                      int FAR *                  pintVal;                           // VT_BYREF|VT_INT.

                      unsigned int FAR *     puintVal;                          // VT_BYREF|VT_UINT.

               };

        };

          我們看到,所有簡(jiǎn)單數(shù)據(jù)類型都可以在VARIANT中找到對(duì)應(yīng)的定義,但是,在多數(shù)的基于C++的系統(tǒng)設(shè)計(jì)中,方法參數(shù)不會(huì)僅僅出現(xiàn)簡(jiǎn)單數(shù)據(jù)類型,類對(duì)象、對(duì)象引用、對(duì)象指針被頻繁的作為參數(shù)來傳遞。

          以類對(duì)象、對(duì)象引用或?qū)ο笾羔樞问酱嬖诘膮?shù),我們稱為復(fù)雜類型參數(shù)。在技術(shù)方案中,所有復(fù)雜類型參數(shù)在ATL接口方法中一律對(duì)應(yīng)接口指針,我們需要提供C++對(duì)象(或引用、指針)和ATL接口指針之間的動(dòng)態(tài)轉(zhuǎn)換功能。下文就復(fù)雜類型作為傳入、傳出參數(shù)分別進(jìn)行討論。

          3.1. 復(fù)雜類型的傳入?yún)?shù)

          ATL接口方法獲取一個(gè)接口指針參數(shù)后,如何將此接口指針轉(zhuǎn)變?yōu)镃++對(duì)象指針?對(duì)于ATL對(duì)象,可以直接取得m_pCPPObj變量,而接口指針卻不能。所以,需要提供一種途徑,從ATL接口指針獲取ATL組件的m_pCPPObj變量值。

          我們的設(shè)計(jì)是,為每個(gè)ATL組件提供一個(gè)基接口ICPPObjSeeker,實(shí)現(xiàn)對(duì)綁定C++對(duì)象指針(即m_pCPPObj)的查詢方法HandleCPPObj。任意ATL接口都從該基接口派生,都可以調(diào)用HandleCPPObj方法。

          在前文就生命周期進(jìn)行討論時(shí),曾提到這樣一種情況:客戶創(chuàng)建了一個(gè)組件,然后送交集合型組件管理。在集合型組件獲取外部創(chuàng)建的組件的同時(shí),需要:

          l 取得后者的C++對(duì)象指針。集合型組件對(duì)元素組件管理的實(shí)質(zhì)是通過集合型C++對(duì)象對(duì)元素的C++對(duì)象進(jìn)行管理,而集合型ATL對(duì)象和元素ATL對(duì)象之間并沒有直接聯(lián)系

          l 修改新加入元素組件的維護(hù)標(biāo)識(shí)

          因此,我們?yōu)镮CPPObjSeeker接口添加PostCPPObj方法,用于實(shí)現(xiàn)以上功能。

          ICPPObjSeeker接口idl定義如下所示,因?yàn)镮CPPObjSeeker接口和HandleCPPObj、PostCPPObj方法實(shí)際上都應(yīng)用于內(nèi)部,所以使用“hidden”屬性對(duì)外隱藏:

          [

               object,

               uuid(1E9F7F79-936D-4680-9F8E-34A7DCCFF818),

               dual,

               hidden,

               helpstring("ICPPObjSeeker Interface"),

               pointer_default(unique)

          ]

        interface ICPPObjSeeker : IDispatch

        {

               [id(1), helpstring("取得C++對(duì)象的指針"), hidden]

                      HRESULT HandleCPPObj([out, retval] long* pCPPObj);

               [id(2), helpstring("取得C++對(duì)象的指針,客戶程序不再負(fù)責(zé)對(duì)C++對(duì)象生命周期的維護(hù)"), hidden]

                      HRESULT PostCPPObj([out, retval] long* pCPPObj);

        };

        ICPPObjSeeker接口的方法可以放在CCPP2ATLTemplateBase模板基類中統(tǒng)一實(shí)現(xiàn):

        template <class T>

        class CCPP2ATLTemplateBase :

        {

                      ……

               /**********************************************************

                 HandleCPPObj函數(shù),由ICPPObjSeeker接口定義,

                 負(fù)責(zé)取得ATL接口中的C++對(duì)象指針

               **********************************************************/

               STDMETHODIMP HandleCPPObj(long *pCPPObj)

               {

                      AFX_MANAGE_STATE(AfxGetStaticModuleState())

                      *pCPPObj = (long)m_pCPPObj;

                      return S_OK;

               }

               /**********************************************************

                 PostCPPObj函數(shù),由ICPPObjSeeker接口定義,

                 負(fù)責(zé)取得ATL接口中的C++對(duì)象指針,

                 同時(shí)標(biāo)記對(duì)象為內(nèi)部維護(hù),客戶不再負(fù)責(zé)對(duì)象的生命周期管理

               **********************************************************/

               STDMETHODIMP PostCPPObj(long *pCPPObj)

               {

                      AFX_MANAGE_STATE(AfxGetStaticModuleState())

                      *pCPPObj = (long)m_pCPPObj;

                      if (m_bInnerManage == FALSE) {

                             m_bInnerManage = TRUE;

                             m_pCPPObj->m_pAssociATLUnk = this;

                             m_pCPPObj->m_pAssociATLUnk->AddRef();

                      }

                      return S_OK;

               }

               };

          現(xiàn)在,所有的接口都不再直接從IDispatch派生,而改從ICPPObjSeeker派生,因此,IDispatch的實(shí)現(xiàn)也應(yīng)該在實(shí)現(xiàn)ICPPObjSeeker接口的同一級(jí)或下級(jí)中提供。為了包容IDispatch,我們將ATL模板基類稍作改動(dòng):

        template <class T, class Q, const IID* piid, const GUID* plibid = &CComModule::m_libid>

        class ATL_NO_VTABLE CCPP2ATLTemplateBase :

               public IDispatchImpl<Q, piid, plibid>

        {

               ……

        };

          在從該模板類派生ATL類時(shí),將ATL Wizard自動(dòng)生成的對(duì)IDispatch接口的實(shí)現(xiàn)注釋,而使用新定義的CCPP2ATLTemplateBase,如下代碼片斷所示:

        class ATL_NO_VTABLE CATLXX :

               ……,

               // 將ATL Wizard生成的對(duì)IDispatch接口的支持注釋

        //     public IDispatchImpl<IXX, &IID_IXX, &LIBID_CPP2ATLLib>,

               // 添加ATL模板基類

               public CCPP2ATLTemplateBase<CImplementXX, IXX, &IID_IXX, &LIBID_CPP2ATLLib>

        {

               ……

        }

          3.2. 復(fù)雜類型的傳出參數(shù)

          從C++指針轉(zhuǎn)換為接口指針基本上不存在困難,為方便使用,我們提供一個(gè)基于本技術(shù)方案的宏定義,如下代碼所示:

        /**********************************************************

          從C++指針獲取對(duì)應(yīng)ATL接口的宏

          傳入:C++指針,對(duì)應(yīng)的ATL類名,接口IID

          傳出:接口指針,執(zhí)行狀態(tài)HRESULT

        **********************************************************/

        #define CPPOBJ_TO_COM_INTERFACE(pCPPObj, CATLClass, IID_IDefine, ppInterface, hResult ) \

               { \

               ASSERT(pCPPObj != NULL); \

               if (pCPPObj->m_pAssociATLUnk != NULL) \

               { \

                      hResult =  pCPPObj->m_pAssociATLUnk-> \

                             QueryInterface(IID_IDefine, (void **)ppInterface); \

                      ATLASSERT(SUCCEEDED(hResult)); \

               } \

               else \

               { \

                      CComObject<CcomATLClass>* pComObj; \

                      hResult = CComObject<CcomATLClass>::CreateInstance(&pComObj); \

                      ATLASSERT(SUCCEEDED(hResult)); \

                      hResult = pComObj-> \

                             QueryInterface(IID_IDefine, (void **)ppInterface); \

                      ATLASSERT(SUCCEEDED(hResult)); \

                      if (hResult == S_OK) \

                             pComObj->Link2CPPObj(pCPPObj, *ppInterface); \

               }\

        }

          4.   接口的繼承與多態(tài)

          C++類的繼承應(yīng)用十分廣泛,動(dòng)態(tài)化后的組件應(yīng)該保留原C++類之間的繼承關(guān)系。在我們的技術(shù)方案中,C++類和接口一一對(duì)應(yīng),C++類的繼承關(guān)系也應(yīng)該體現(xiàn)在各個(gè)接口上,如下圖所示:

          

          4.1. 支持繼承的系列ATL模板基類

          實(shí)現(xiàn)接口繼承的實(shí)質(zhì)是為派生ATL類添加基接口,而為一個(gè)ATL類添加接口的實(shí)質(zhì)則是:

          l 修改IDL文件,體現(xiàn)接口的繼承關(guān)系

          l 在ATL類中提供接口實(shí)現(xiàn)

          修改IDL文件很簡(jiǎn)單,只需要更改派生接口的基接口即可。在ATL類中添加基接口的實(shí)現(xiàn)倒頗費(fèi)思量,我們的做法是:

          l 擴(kuò)展ATL模板基類的意義,每一個(gè)ATL組件類都對(duì)應(yīng)一個(gè)模板基類,都從該模板基類派生

          l 派生類的模板基類,從基類的模板基類中派生;CCPP2ATLTemplateBase是模板派生樹的根節(jié)點(diǎn),所有的模板都派生自CCPP2ATLTemplateBase

          l  所有的接口方法,都在對(duì)應(yīng)的模板基類中實(shí)現(xiàn)

          ATL派生類繼承自它對(duì)應(yīng)的模板基類,這個(gè)模板基類又繼承自ATL基類對(duì)應(yīng)的模板基類,而在ATL基類的模板基類中提供了基接口的實(shí)現(xiàn)。所以,ATL派生類最終繼承了基接口的實(shí)現(xiàn)。C++類、ATL類、各模板基類的繼承關(guān)系如下圖所示:

          

          假定IBaseItf是基接口,IInheritItf是派生接口。ATL基類對(duì)應(yīng)的模板基類定義如下:

        /****************************************************************************

          模板類CAtlBaseItf,提供了IBaseItf的實(shí)現(xiàn),

          用于將IBaseItf接口作為基接口共供其它接口繼承

        ****************************************************************************/

        template <class T, class Q, const IID* piid, const GUID* plibid = &CComModule::m_libid>

        class ATL_NO_VTABLE CAtlBaseItf : public CCPP2ATLTemplateBase<T, Q, piid, plibid>

        {

        public:

               // 基接口方法“BaseFunc”,在此模板類內(nèi)實(shí)現(xiàn)

               STDMETHOD(BaseFunc)()

               {

                      m_pCPPObj->BaseFunc();

                      return S_OK;

               }

        };

        ATL派生類對(duì)應(yīng)的模板基類定義如下:

        /****************************************************************************

          模板類CAtlInheritItf,繼承了基接口IBaseItf方法的實(shí)現(xiàn),

          同時(shí)提供了IInheritItf的實(shí)現(xiàn),可以將IInheritItf接口作為基接口共供其它接口繼承

        ****************************************************************************/

        template <class T, class Q, const IID* piid, const GUID* plibid = &CComModule::m_libid>

        class ATL_NO_VTABLE CAtlInheritItf : public CAtlBaseItf<T, Q, piid, plibid>

        {

        public:

               // 派生接口方法“InheritFunc”,在此模板類內(nèi)實(shí)現(xiàn)

               STDMETHOD(InheritFunc)()

               {

                      m_pCPPObj->InheritFunc();

                      return S_OK;

               }

        };

        更改IInheritItf接口的IDL定義:

        [

               object,

               uuid(8F3902DF-DA55-4802-AB8A-958AFF45B2F4),

               dual,

               helpstring("IBaseItf Interface"),

               pointer_default(unique)

        ]

        // 基接口從ICPPObjSeeker派生

        interface IBaseItf : ICPPObjSeeker

        {

               [id(1), helpstring("IBaseItf Method")] HRESULT BaseFunc();

        };

        [

               object,

               uuid(AFEBD472-4BEC-45CE-A5A2-E37537C4744A),

               dual,

               helpstring("IInheritItf Interface"),

               pointer_default(unique)

        ]

        // IInheritItf接口從IBaseItf接口派生

        interface IInheritItf : IBaseItf

        {

               [id(11), helpstring("IInheritItf Method")] HRESULT InheritFunc();

        };

        最后,更改ATL派生類的模板基類:

        class ATL_NO_VTABLE CATLInherit :

               ……,

               public CAtlInheritItf<CInheritItfImplement, IInheritItf, &IID_IInheritItf, &LIBID_CPP2ATLLib>

        {

               ……

        };

          現(xiàn)在,通過IInheritItf,我們可以使用IBaseItf的所有方法,實(shí)現(xiàn)了接口的繼承。

          4.2. 接口的多態(tài)性

          在實(shí)現(xiàn)接口的繼承后,要展現(xiàn)接口的多態(tài)性就很容易了,只需在ATL派生類聲明的接口映射表中添加基接口表項(xiàng)即可:

        class ATL_NO_VTABLE CATLInherit :

               ……,

               public CAtlInheritItf<CInheritItfImplement, IInheritItf, &IID_IInheritItf, &LIBID_CPP2ATLLib>

        {

               ……

               BEGIN_COM_MAP(CInheritItf)

               COM_INTERFACE_ENTRY(IInheritItf)

               COM_INTERFACE_ENTRY(IBaseItf)

               ……

        END_COM_MAP()

               ……

        };

          就象C++中基類指針?biāo)宫F(xiàn)的多態(tài)性一樣,一個(gè)“IBaseItf *”型指針可以完全操縱IInheritItf接口,而不需要知道真正的接口類型。

          5.  

          至此,我們的技術(shù)方案全部介紹完畢。C++基類CCPP2ATLObjBase、ATL模板基類CCPP2ATLTempBase和基接口ICPPObjSeeker是方案中的關(guān)鍵技術(shù)。CCPP2ATLObjBase配合CCPP2ATLTempBase,完善了組件對(duì)象生命周期的機(jī)制;通過基接口ICPPObjSeeker,我們可以從任意接口反向查詢C++對(duì)象;CCPP2ATLTempBase提供了C++對(duì)象和ATL組件的自由綁定功能,封裝了IDispatch接口的實(shí)現(xiàn),而進(jìn)一步定義的ATL模板基類繼承體系則極大的方便了接口的自由繼承。

          在本文快結(jié)束的時(shí)候,我們不得不特別提到Microsoft的“.Net FrameWork”!.Net”開發(fā)框架的推出,的確解決了COM技術(shù)的許多困惑,也包括本技術(shù)方案所要解決的一些技術(shù)問題。然而“.Net Framework”是一個(gè)“改朝換代”的變化,要想一步將原來基于C++的系統(tǒng)(尤其是大型系統(tǒng))完全移植到“.Net”平臺(tái)上是不可想象的,其工作量不亞于重新開發(fā),所以Microsoft特別推薦從COM技術(shù)到“.Net”平臺(tái)的平滑移植。由此看來,本文提出的動(dòng)態(tài)組件化的技術(shù)更顯得可貴,它從工程化的角度,著眼于實(shí)際應(yīng)用,解決了從面向?qū)ο蟮腃++到基于組件的COM技術(shù)的許多問題,既充分保護(hù)了原有系統(tǒng)的積累,又為這些系統(tǒng)搭上日益發(fā)展的“.Net”快車提供了可能。


          參考文獻(xiàn)

          《COM原理與應(yīng)用》,潘愛民 著,清華大學(xué)出版社

          《COM本質(zhì)論(Essential COM)》,Don Box 著,潘愛民 譯,中國(guó)出版社

          《深入解析ATL(ATL Internals)》,Brent Rector、Chris Sells 著,潘愛民、新語 譯,中國(guó)電力出版社

          《設(shè)計(jì)模式-可復(fù)用面向?qū)ο筌浖幕A(chǔ)(Design Patterns-Elements of Reusable Object-Oriented Software)》,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 著,李英軍、馬曉星、蔡敏、劉建中 等譯

        【C++類的動(dòng)態(tài)組件化技術(shù)】相關(guān)文章:

        高新技術(shù)產(chǎn)品動(dòng)態(tài)競(jìng)爭(zhēng)上風(fēng)分析06-02

        機(jī)電一體化技術(shù)論文02-22

        配網(wǎng)自動(dòng)化技術(shù)風(fēng)險(xiǎn)及控制研究05-25

        機(jī)械工程自動(dòng)化技術(shù)研究05-04

        工業(yè)技術(shù)體系知識(shí)自動(dòng)化論文04-12

        電氣自動(dòng)化節(jié)能設(shè)計(jì)技術(shù)應(yīng)用研究06-12

        企業(yè)信息化安全技術(shù)研究01-08

        機(jī)電一體化技術(shù)論文 15篇02-22

        信息化技術(shù)在高校教務(wù)管理中的運(yùn)用論文05-05

        配網(wǎng)自動(dòng)化技術(shù)的快速?gòu)?fù)電策略05-02

        国产高潮无套免费视频_久久九九兔免费精品6_99精品热6080YY久久_国产91久久久久久无码

        1. <tt id="5hhch"><source id="5hhch"></source></tt>
          1. <xmp id="5hhch"></xmp>

        2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

          <rp id="5hhch"></rp>
              <dfn id="5hhch"></dfn>