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)中,應包含一個DllGetClassObject出口函數,此函數供系統的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);
}
... ...

0

收藏

zhangjuanyong

24篇文章,17W+人氣,0粉絲

Ctrl+Enter 發佈

發佈

取消

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章