深入探索MS COM開發框架 之 MFC和ATL(三)

1.        事實上,組件類獲得接口映射表是通過GetInterfaceMap()靜態成員函數先
獲得interfaceMap結構變量,此變量的第二個值便是接口映射表的入口.
InterfaceMap的第一個值保存了基類的_GetBaseInterfaceMap函數指針.
爲在組件類中找不到接口的定義時,通過_GetBaseInterfaceMap
獲得基類的interfaceMap,從而獲得基類的接口映射表入口….
就這樣不斷上溯..
         
          通過這些宏,我們實現了接口映射表,併爲在類層次中輾轉提供了
          方法.這樣,組件類就可以通過這張表實現接口的查詢和地址定位了.
 
然而這裏還有一個大問題
我們說,任何一個接口必須實現AddRef、Release、QueryInterface.
(注2:事實上,接口的定義在Idispatch出現後,有了一定的轉變,你可將任何
實現了方法和數據的結合看作接口.)…可這裏沒見什麼關於這方面的定義啊..
不錯,這裏還有一個”大陰謀”呢…. 關於這個還得從
#define INTERFACE_PART(theClass, iid, localClass) /
{ &iid, offsetof(theClass, m_x##localClass) }, /
//填充接口影射表
說起….
在實際的工程中,默認的上面的localClass將會被Dispatch取代.
Dispatch又爲何物 ? 事實上,在CDK1.0中,COM開發不用上面這些宏,
在那裏你可以清楚的看到問題的實質,沒辦法,時代變了,就面前的情況來
探討吧…
上面Dispatch的位置,規定置入實現接口的嵌套類的…
那麼毫無疑問Dispatch就是嵌套類啦.可是你說,這是哪裏來的 ?
事實上,在CCmdTarget中,有這麼一片段:
 struct XDispatch
 {
  DWORD m_vtbl;   // place-holder for IDispatch vtable
#ifndef _AFX_NO_NESTED_DERIVATION
  size_t m_nOffset;
#endif
 } m_xDispatch;
它將成爲實現接口的嵌套類.
這怎麼可能成爲嵌套類呢 . .它什麼也沒有啊…簡簡單單的結構而已啊….
事實在內部,存在一樣的虛表vtable指派行爲,正是這種行爲使得m_xDispatch徹底的
變了,這種行爲的引爆器就是組件類構造函數中的EnableAutomation();
代碼如下:
void CCmdTarget::EnableAutomation()
{
 ASSERT(GetDispatchMap() != NULL);   // must have DECLARE_DISPATCH_MAP
 
 // construct an COleDispatchImpl instance just to get to the vtable
 COleDispatchImpl dispatch;
 
 // vtable pointer should be already set to same or NULL
 ASSERT(m_xDispatch.m_vtbl == NULL||
  *(DWORD*)&dispatch == m_xDispatch.m_vtbl);
 // sizeof(COleDispatchImpl) should be just a DWORD (vtable pointer)
 ASSERT(sizeof(m_xDispatch) == sizeof(COleDispatchImpl));
 
 // copy the vtable (and other data) to make sure it is initialized
 m_xDispatch.m_vtbl = *(DWORD*)&dispatch;
 *(COleDispatchImpl*)&m_xDispatch = dispatch;
}
 
PART2 深入CCmdTarget看一看COM三大元素的實現
 
   我們之所以深入到CCmdTarget
   不是想只是爲了那簡簡單單的實現
   我們想知道MFC對COM的一大美景:聚合
   是怎麼實現的.
   二話不說,擺出架勢先 :
public:
 // data used when CCmdTarget is made OLE aware
 long m_dwRef;
 LPUNKNOWN m_pOuterUnknown;  // external controlling unknown if != NULL
 DWORD m_xInnerUnknown;  // place-holder for inner controlling unknown
 
public:
 // advanced operations
 void EnableAggregation();       // call to enable aggregation
 void ExternalDisconnect();      // forcibly disconnect
 LPUNKNOWN GetControllingUnknown();
  // get controlling IUnknown for aggregate creation
 
 // these versions do not delegate to m_pOuterUnknown
 DWORD InternalQueryInterface(const void*, LPVOID* ppvObj);
 DWORD InternalAddRef();
 DWORD InternalRelease();
 // these versions delegate to m_pOuterUnknown
 DWORD ExternalQueryInterface(const void*, LPVOID* ppvObj);
 DWORD ExternalAddRef();
 DWORD ExternalRelease();
 
 // implementation helpers
 LPUNKNOWN GetInterface(const void*);
 LPUNKNOWN QueryAggregates(const void*);
 
 // advanced overrideables for implementation
 virtual BOOL OnCreateAggregates();
 virtual LPUNKNOWN GetInterfaceHook(const void*);
從上面的聲明中,你可發現:
   這裏聲明瞭兩套標準接口方法,
   Externalxx對應於COM模型中的委託IUnknown
  而Internalxx對應COM模型中的非委託Iunknown
 有了這兩套接口方法,聚合的實現就OK了…..
 關於聚合的實現細節,欲知詳情,請參閱相關專門
  的書籍,這裏不再贅述.
 
通過PART1和PART2關於接口的基礎構造已經完成.
 
PART3------類廠的由來
 
不用說,COM對象的創建是需要類廠的.
---------------------------------------------------------------------------------------------------------
DECLARE_OLECREATE(CSAM)宏剖析
-----------------------------------------------------------------------------
#define DECLARE_OLECREATE(class_name) /
public: /
 static AFX_DATA COleObjectFactory factory; /
   //定義類廠對象…
 static AFX_DATA const GUID guid; /
   //組件類的GUID
 
------------------------------------------------------------------------------------------------------------
                          IMPLEMENT_OLECREATE 宏剖析
                     ------------------------------------------------------------------
#define IMPLEMENT_OLECREATE(class_name, external_name, l, w1, w2, b1,/
 b2, b3, b4, b5, b6, b7, b8) /
 AFX_DATADEF COleObjectFactory class_name::factory(class_name::guid, /
  RUNTIME_CLASS(class_name), FALSE, _T(external_name)); /
//這裏要注意的是external_name:ProgID
 AFX_COMDAT const AFX_DATADEF GUID class_name::guid = /
  { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; /
//將組件類的CLSID賦予組件類的成員變量guid.
-----------------------------------------------------------------------------------------------------------
IMPLEMENT_OLECREATE(CSAM, "MFCCOM.SAM",
0x43d242f9, 0x4f7e, 0x4cbb, 0xae, 0xda, 0x77, 0x8d, 0xa1, 0x16, 0xd0, 0xd9)
說明:我們知道,在創建組件類對象時,首先由App核心獲得當前狀態,
從中取出類廠表,依據CLSID獲得相應的類廠對象指針.正是在這裏
將類廠和CLSID、ProID等信息關聯.
 
PART4-------自動化
 
事實上,自動化也是一個極大的主題.自動化技術增強了組件的環境適應性.
對於MFC中的自動化組件,由於默認的接口爲dispinterface,所以它對方法的
訪問一律採用分發的手段.不同於你意識中,一直牢記的通過vtable[_index]
來訪問方法.在使用組件的過程中,首先獲得Idispatch接口,然後調用Idispatch
的方法GetIDsOfNames獲得programmers希望的方法的令牌(ID),最後通過
Idispatch的方法Invoke來執行.這種技術使得腳本和宏環境可以使用COM
對象,不過對於有預先編譯能力的環境來說,會使得組件系統的性能大打
折扣.因爲它多了一個反覆調用的中間層.
關於Dispatch的支持,基本上構建思路原理同於
上述的Interface..
-----------------------------------------------------------------------------------------------------------------------
DECLARE_DISPATCH_MAP()
                          BEGIN_DISPATCH_MAP(CSAM, CCmdTarget)[.cpp]
 DISP_PROPERTY_NOTIFY(CSAM, "Fook", m_Fook, OnFookChanged, VT_R4)
 DISP_FUNCTION(CSAM, "Post", Post, VT_R4, VTS_NONE)
END_DISPATCH_MAP()
       宏剖析
--------------------------------------------------------------------
#ifdef _AFXDLL
#define DECLARE_DISPATCH_MAP() /
private: /
 static const AFX_DISPMAP_ENTRY _dispatchEntries[]; /
 static UINT _dispatchEntryCount; /
 static DWORD _dwStockPropMask; /
protected: /
 static AFX_DATA const AFX_DISPMAP dispatchMap; /
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章