獲得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; /