MFC對COM介面編寫的支援分析 (轉)

gugu99發表於2008-05-17
MFC對COM介面編寫的支援分析 (轉)[@more@] FMD STUDIO 之 VC++ 之 技術篇 之 : 【script: history.back()'>回上一頁】 編號 主 題 來 源 收錄時間 4 MFC對COM介面編寫的支援分析 自撰 00.05.08

MFC對COM的支援分析

MFC採用C++中巢狀類定義的方法實現COM介面。

為了簡化其中的編寫。又採用了大量的宏定義。

以下將從巢狀類實現的原始寫法開始,分析MFC中對COM的支援

一、COM概述

1.COM提供了一種簡潔的二進位制使用方法。為了符合COM規範。用VC++編寫的物件(類) 程式碼必須在編譯後,能按照COM規範的格式提供"介面"。

2.關於介面,介面是一個指標,指標指向一個表格,表格內包含了能操作物件的一組的指標。

3.C++類定義結構對介面的支援。

為了支援多型,包含虛擬函式的C++物件都將包含一個指向虛擬函式表的指標,這種結構符合COM介面規範。

而操作同一物件的介面可以有多個。C++類定義中,可以用兩種方案實現多個介面。

一種是多繼承結構,一個類從多個介面類派生。

另一種是巢狀類定義結構。

ATL採用前者,MFC採用後者。

4.除了規範方法外,COM還規範了物件的建立、生存期等。

每個COM物件對應還有一個"類廠"物件,類廠物件中包含CreateInstance(..)成員,負責物件的建立,建立後,介面才有使用的可能。

而包含COM物件的(DLL)中,應包含一個DllGetClass出口函式,此函式供的COM庫呼叫,在DllGetClassObject中,類廠被建立,類廠介面返回給系統COM庫。

COM庫根據類廠介面,呼叫CreateInstance建立COM物件,返回介面給。使用者使用介面呼叫介面成員操作物件。

5.總結

DLL專案中要提供COM物件,需要用C++類的形式定義物件的屬性和行為,但此類對使用者是不可見的,使用者透過介面操作類物件,因此,類的構造過程中,需要提供合適的介面,提供給使用者。

為了和系統的COM庫相,配合,DLL應該提供規定的幾個輸出函式。並提供指定物件的類廠,以及類廠中物件的建立等。

二、用C++類巢狀定義的結構提供類介面。

如果一個類定義包含一組虛擬函式,則其物件在中結構符合介面結構。

故在整個物件的封裝類中,巢狀定義幾個包含特定虛擬函式組合的類,以及巢狀類成員。

巢狀類的結構設定成欲提供介面的結構。(派生自I...)

例:

1.定義部分:

①介面結構的定義(虛擬函式組合)

class ISpellCheck : public IUnknown //介面一 { public : virtual BOOL __stdcall CheckWord(String, String*) = 0; }; class IDictionary : public IUnknown //介面二 { public : virtual BOOL __stdcall Initialize() = 0; ... ... };


②在類定義中,巢狀定義以上介面類派生類及成員。

class CDictionary { ...QueryInterface(...); file://IUnknown需要用到的呼叫。 ...AddRef(); ...Release(); ... ... class XDictionaryobj:public IDictionary //介面定義 { virtual ULONG _stdcall AddRef(); ... ... }m_dictionaryobj; //介面成員 (其記憶體結構包含一個虛擬函式表指標,此正是介面結構所要求的。 ... ... class XSpellCheckobj:public ISpellCheck { virtual ... ... ... }m_spellCheckObj;//介面成員 ... ... }


巢狀類中需要使用原類(CDictionary),故各巢狀類中有成員儲存原類的指標。

③需要定義一個與上類配合的類廠物件類。

2.實現部分

①各成員的實現

②編寫合適的IUnknown 實現

IUnknown其實是在原類中實現的。

在各巢狀類定義中,AddRef,Release,QueryInterface都呼叫原類中定義的公共AddRef、等函式。

QueryInterface函式中,根據傳入介面ID,返回類中介面成員(m_dictionaryobj 等等)的地址。

以上結構可以構造COM介面,但書寫及維護困難。

MFC採用了大量的宏來簡化巢狀類的書寫。由於宏的使用,在形式上看,與訊息對映有些相識,但其實際編碼,還是巢狀類。

③實現類廠。類廠中建立物件。

三、MFC中,介面定義

1.思路

①一個類中,包含多個介面,故包含多個巢狀類,且QueryInterface中要作多次判斷。

可以抽象成為陣列資料型別(對映表),新增介面簡化為新增表項,查詢介面簡化為查表。

②巢狀類的書寫複雜,用宏定義的方法簡化。

③建立通用類廠

2.實現

①在類定義中新增介面對映表(DECLARE_INTERFACE_MAP),實現中新增對映表填表BEGIN_INTERFACE_MAP、END_INTERFACE_MAP等。

表項內容包括 介面標識(IID),介面成員的位置(相對物件起始地址的偏移量)

②巢狀類定義宏BEGIN_INTERFACE_PART、END_INTERFACE_PART_STATIC等。

②類定義中包含通用類廠成員COleObjectFactory factory,以及COM物件識別符號成員guid,這些由宏DECLARE_OLECREATE完成

3.示例:

介面結構定義略。

定義:

class CDictionaryObj : public CCmdTarget //由CCmdTarget派生,CCmdTarget提供了介面相關支援 { DECLARE_DYNCREATE(CDictionaryObj) //動態建立,類廠要動態建立物件,故必需。 ... ... DECLARE_OLECREATE(CDictionaryObj) //新增類廠成員,COM物件標識成員。 DECLARE_INTERFACE_MAP() //新增介面對映表的宣告,以及相關函式的宣告。 //用此表格記錄實現的介面,以及介面的位置等。 //巢狀類的宣告 BEGIN_INTERFACE_PART(Dictionary, IDictionary) //AddRef、Release等的宣告包含在宏中。 INIT_INTERFACE_PART(CDictionary, Dictionary) STDMETHOD_(BOOL, Initialize)(); ... ... END_INTERFACE_PART_STATIC(Dictionary) //第二個巢狀類 BEGIN_INTERFACE_PART(SpellCheck, ISpellCheck) INIT_INTERFACE_PART(CDictionary, SpellCheck) STDMETHOD_(BOOL, CheckWord)(LPOLESTR, LPOLESTR *); END_INTERFACE_PART_STATIC(SpellCheck) ... ... };


實現:

IMPLEMENT_DYNCREATE(CDictionaryObj, CCmdTarget) //介面標識 extern "C" const IID IID_Dictionary = { 0x54bf6568, 0x1007, 0x11d1, { 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ; extern "C" const IID IID_SpellCheck = { 0x54bf6569, 0x1007, 0x11d1, { 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ; //介面對映表的填寫 BEGIN_INTERFACE_MAP(CDictionaryObj, CCmdTarget) INTERFACE_PART(CDictionaryObj, IID_Dictionary, Dictionary) INTERFACE_PART(CDictionaryObj, IID_SpellCheck, SpellCheck) END_INTERFACE_MAP() //類廠實現,guid的賦值。 // {54BF6567-1007-11D1-B0AA-444553540000} IMPLEMENT_OLECREATE(CDictionaryObj, "Dictionary.Object", 0x54bf6567, 0x1007, 0x11d1, 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00) //各函式的實現 STDMETHODIMP_(ULONG) CDictionaryObj::XDictionary::AddRef() { y) //宏中定義了pThis指標,有介面對映表中記錄的偏移計算出COM物件的指標,呼叫物件類成員。 METHOD_PROLOGUE_EX_(CDictionaryObj, Dictionar return (ULONG)pThis-> ExternalAddRef(); //呼叫CCmdTarget基類中對IUnknown的實現。 //在被聚合情況下,ExternalAddRef呼叫外部IUnknownd 的實現 //通常情況下,呼叫InternalAddRef. } STDMETHODIMP CDictionaryObj::XDictionary::QueryInterface( REFIID iid, LPVOID* ppvObj) { METHOD_PROLOGUE_EX_(CDictionaryObj, Dictionary) //介面查詢直接呼叫CCmdTarget的實現,其中,透過查介面對映表得知。 return (HRESULT)pThis-> ExternalQueryInterface(&iid, ppvObj); } ... ...



【: history.back()'>回上一頁】

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1004167/,如需轉載,請註明出處,否則將追究法律責任。

相關文章