




已閱讀5頁(yè),還剩31頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
ATL接口映射宏詳解序言: 這幾天看了看ATL的接口映射宏,不知不覺看得比較深入了,突然就萌發(fā)了把它寫出來(lái)的想法。ATL中定義了很多接口映射宏,有幾個(gè)還是比較重要的,雖然好象沒有必要把它所有的細(xì)節(jié)都弄得很清楚,但深入學(xué)習(xí)的過(guò)程中也可以順帶學(xué)一學(xué)其他的ATL類,對(duì)它的機(jī)制也可以更清楚一些,應(yīng)該還是會(huì)有些好處的吧。我按照我學(xué)習(xí)的過(guò)程把它寫出來(lái),也 不知道大家能不能看懂。想模仿一下侯老師的手筆力爭(zhēng)把其內(nèi)部細(xì)節(jié)解釋清楚,但也不敢大言不慚的美其名曰“深入淺出”,呵呵,只希望能對(duì)大家有所幫助了。 以后將分別介紹ATL中各個(gè)形式為COM_INTERFACE_ENTRY_XX的接口映射宏并將按照從易到難的順序講解,每一部分都將建立在前一部分的基礎(chǔ)上。每一部分都將通過(guò)分析實(shí)際的調(diào)用函數(shù)堆棧來(lái)進(jìn)行分析,堆棧的寫法是從下向上。文中所涉及的代碼都為略寫,只列出相關(guān)部分。 一、COM_INTERFACE_ENTRY(x) 首先我們從一個(gè)最典型的應(yīng)用開始: 定義一個(gè)最簡(jiǎn)單的ATL DLL: class ATL_NO_VTABLE CMyObject : public CComObjectRootEx, public CComCoClass, public IDispatchImpl . BEGIN_COM_MAP(CMyObject) COM_INTERFACE_ENTRY(IMyObject) /一個(gè)雙接口 COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() . ; 編寫一段最簡(jiǎn)單的查詢接口代碼: IUnknown *pUnk; IMyObject *pMyObject; CoCreateInstance(CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void *)&pUnk); pUnk-QueryInterface(IID_IMyObject, (void *)&pMyObject); 執(zhí)行客戶代碼,首先我們看看組件對(duì)象是如何被創(chuàng)建的。 函數(shù)調(diào)用堆棧一: 4. 3.ATL:CComCreator ATL:CComObject :CreateInstance(.) 2.ATL:CComCreator2 ATL:CComCreator ATL:CComObject , ATL:CComCreator ATL:CComAggObject :CreateInstance(.) 1.ATL:CComClassFactory:CreateInstance(.) 4.ATL:AtlModuleGetClassObject(.) 9.ATL:AtlInternalQueryInterface(.) 8.ATL:CComObjectRootBase:InternalQueryInterface(.) 7.ATL:CComClassFactory:_InternalQueryInterface(.) 6.ATL:CComObjectCached:QueryInterface(.) 5.ATL:CComCreator : CreateInstance(.) 4.ATL:AtlModuleGetClassObject(.) 3.ATL:CComModule:GetClassObject(.) 2.DllGetClassObject(.) 1.CoCreateInstance(.)(客戶端) 解釋如下: 1: CoCreateInstance(CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void *)&pUnk); 其內(nèi)部將調(diào)用OLE API函數(shù)CoGetClassObject(), 而CoGetClassObject則會(huì)通過(guò) Load Library(.)裝入DLL,并調(diào)用DLL中的DllGetClassObject()函數(shù)。2: STDAPI DllGetClassObject(REFCLSID closed, REFIID rid, LPVOID* pip)return _Module.GetClassObject(closed, rid, pip); 其中值得注意的是_Module變量,在DLL中定義了全局變量: CComModule _Module; ATL通過(guò)一組宏: BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_MyObject, CMyObject) END_OBJECT_MAP() #define BEGIN_OBJECT_MAP(x) static _ATL_OBJMAP_ENTRY x = #define OBJECT_ENTRY(clsid, class) &clsid, class:UpdateRegistry, class:_ClassFactoryCreatorClass:CreateInstance, /關(guān)鍵 class:_CreatorClass:CreateInstance, NULL, 0, class:GetObjectDescription, class:GetCategoryMap, class:ObjectMain , #define END_OBJECT_MAP() NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL; 生成一個(gè)靜態(tài)全局_ATL_OBJMAP_ENTRY型數(shù)組:ObjectMap; 然后ATL又在 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/ . _Module.Init(ObjectMap, hInstance, &LIBID_TEST2Lib); . 中初始化_Module /注意在有的情況下是在InitInstance()中初始化_Module 那么_Module初始化都做了些什么呢,其實(shí)他什么也沒做,在CComModule:Init中,它調(diào)用AtlModuleInit(_ATL_MODULE* pM, _ATL_OBJMAP_ENTRY* p, HINSTANCE h),在其中關(guān)鍵的只有一句:pM-m_pObjMap = p;可見_Module僅僅是把這個(gè)全局對(duì)象映射數(shù)組 ObjectMap給存了起來(lái)。那么為什么可以通過(guò)_Module.GetClassObject得到類廠呢?其實(shí)關(guān)鍵在于我們的組件CMyObject繼承的又一個(gè)基類CComCoClass! 在CComCoClass中缺省定義了一個(gè)宏DECLARE_CLASSFACTORY()而 #define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(CComClassFactory)#define DECLARE_CLASSFACTORY_EX(cf) typedef CComCreator ccomobjectcached _ClassFactoryCreatorClass; CComCreator,CComObjectCached我們暫且不管,但一看到CComClassFactory,顧名思義,我們就知道我們要的類廠終于出現(xiàn)了!每個(gè)組件內(nèi)部原來(lái)都有一個(gè)類廠對(duì)象。繞了一大圈,我們現(xiàn)在已經(jīng)知道了_Module中包含了我們所要的每個(gè)組件的類廠對(duì)象,這對(duì)目前來(lái)說(shuō)已經(jīng)足夠了,現(xiàn)在繼續(xù)路由下去!3: HRESULT CComModule:GetClassObject(REFCLSID rclsid,REFIID riid,LPVOID* pip)return AtlModuleGetClassObject(this, closed, rid, pip); CComModule:GetClassObject的實(shí)現(xiàn)非常簡(jiǎn)單,僅僅是調(diào)用ATL的API函數(shù)。 4: ATLINLINE ATLAPI AtlModuleGetClassObject(_ATL_MODULE* pM, REFCLSID closed, REFIID rid, LPVOID* pip) _ATL_OBJMAP_ENTRY* pEntry = pM-m_pObjMap;/從_Module中取出對(duì)象映射數(shù)組 while (pEntry-pclsid != NULL) if (pEntry-pfnGetClassObject != NULL) & InlineIsEqualGUID(closed, *pEntry-pclsid) if (pEntry-pCF = NULL) hRes = pEntry-pfnGetClassObject(pEntry-pfnCreateInstance, IID_IUnknown, (LPVOID*)&pEntry-pCF); if (pEntry-pCF != NULL) hRes = pEntry-pCF-QueryInterface(rid, pip); break; pEntry = _NextObjectMapEntry(pM, pEntry); 現(xiàn)在好象已經(jīng)有點(diǎn)看不懂了,看來(lái)我們得看看_ATL_OBJMAP_ENTRY的結(jié)構(gòu)了 struct _ATL_OBJMAP_ENTRY const CLSID* pclsid; HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister); _ATL_CREATORFUNC* pfnGetClassObject; _ATL_CREATORFUNC* pfnCreateInstance; IUnknown* pCF; DWORD dwRegister; _ATL_DESCRIPTIONFUNC* pfnGetObjectDescription; _ATL_CATMAPFUNC* pfnGetCategoryMap; pclsid很清楚就代表著我們組件的CLSID;pfnGetClassObject我們也已經(jīng)知道了它就是CMyObject:_ClassFactoryCreatorClass:CreateInstance(我們組件所包含的類廠對(duì)象的CreateInstance函數(shù));pCF我們也可以猜出它是指向這個(gè)類廠的IUnknown指針,代表這個(gè)類廠對(duì)象是否被創(chuàng)建過(guò),若類廠對(duì)象已經(jīng)存在,就不用再創(chuàng)建新的類廠對(duì)象了?,F(xiàn)在就剩下pfnCreateInstance我們還不明白怎么回事。其實(shí)答案還是在 CComCoClass中! 在CComCoClass中缺省定義了宏DECLARE_AGGREGATABLE(x),這個(gè)宏表示這個(gè)組件既可以是聚集的也可以是非聚集的,關(guān)于聚集的概念我們暫且不理,先看它的定義: #define DECLARE_AGGREGATABLE(x) public: typedef CComCreator2 ccomcreator CComObject , CComCreator ccomaggobject _CreatorClass; 我們看到了一個(gè)熟悉的字符串_CreatorClass, 原來(lái)這還有一個(gè)組件包含的對(duì)象。但還有一個(gè)問(wèn)題我們沒有搞清楚,就是為什么_ClassFactoryCreator和_CreatorClass后面都要跟著一個(gè)CreateInstance? 看來(lái)我們必須先來(lái)看看CComCreator是個(gè)什么東西了。template class CComCreator public: static HRESULT WINAPI CreateInstance(void* pv, REFIID rid, LPVOID* pip) . ; 原來(lái)它里面只有一個(gè)CreateInstance函數(shù),我們現(xiàn)在終于大體明白_ClassFactoryCreatorClass:CreateInstance 表示什么意思了,它就代表CComClassFactory:CreateInstance(.)吧,差不多就是這樣了。那我們?cè)賮?lái)看看CComCreator2有什么不同: template class CComCreator2 public: static HRESULT WINAPI CreateInstance(void* pv, REFIID rid, LPVOID* pip) return (pv = NULL) ? T1:CreateInstance(NULL, rid, pip) : T2:CreateInstance(pv, rid, pip); ; 這個(gè)類與CComCreator很類似,都只有一個(gè)CreateInstance成員函數(shù),從_CreatorClass 中我們可以知道它實(shí)際上包含兩個(gè)類CComObject,CComAggObject的CreateInstance函數(shù)(通過(guò)CComCreator),其中CComObject用于非聚集對(duì)象,CComAggObject用于聚集對(duì)象根據(jù)情況它建立相應(yīng)的對(duì)象。(ATL中實(shí)際生成的組件對(duì)象不是CMyObject,而是 CComObject,CComAggObject或CComPolyObject對(duì)象,這個(gè)概念很重要,但現(xiàn)在暫且不談) 現(xiàn)在我們對(duì)AtlModuleGetClassObject(.)基本已經(jīng)知道是怎么回事了,它就是根據(jù)存在對(duì)象映射數(shù)組中的創(chuàng)建類廠的函數(shù)的地址來(lái)創(chuàng)建類廠。pfnGetClassObject以及 pfnCreateInstance我們基本上都已經(jīng)知道是怎么回事了,但還有一個(gè)問(wèn)題為什么要把pEntry-pfnCreateInstance作為pEntry-pfnGetClassObject(.)中的一個(gè)參數(shù)傳遞?答案在下面呢,讓我們繼續(xù)路由下去!5: CComCreator:CreateInstance(void* pv, REFIID rid, LPVOID* pip) T1* p = NULL; ATLTRY(p = new T1(pv)/創(chuàng)建類廠對(duì)象 if (p != NULL) p-SetVoid(pv); p-InternalFinalConstructAddRef(); hRes = p-FinalConstruct(); p-InternalFinalConstructRelease(); if (hRes = S_OK) hRes = p-QueryInterface(rid, pip); if (hRes != S_OK) delete p; 注意這里的T1是CComObjectCached,這是我們給CComCreator 的模板參數(shù)。我們又一次看到了我們熟悉的操作符new!直到現(xiàn)在我們終于創(chuàng)建了組件的類廠。但還沒完,繼續(xù)往下走,看看SetVoid(pv)里干了些什么? void CComClassFactory:SetVoid(void* pv) m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv; 大家還記得我們?cè)?jīng)把CMyObject:_CreatorClass:CreateInstance作為參數(shù)傳給 pEntry-pfnGetClassObject(.)吧,當(dāng)時(shí)我們不明白是怎么回事,現(xiàn)在已經(jīng)豁然開朗!原來(lái)是類廠需要它來(lái)創(chuàng)建組件對(duì)象!雖然我們只是從字面意思猜出這一點(diǎn),但實(shí)際上也正如我們所預(yù)料的那樣,在CComClassFactory:CreateInstance(.)中,我們看到了m_pfnCreateInstance(pUnkOuter, rid, ppvObj);現(xiàn)在一切都已經(jīng)明白了, ATL為我們創(chuàng)建類廠而作的層層包裝我們都已經(jīng)打開,剩下的創(chuàng)建組件的過(guò)程已經(jīng)是我們很熟悉的過(guò)程了! 但是現(xiàn)在還沒有完,我們還需要為類廠對(duì)象查詢一個(gè)IUnknown指針,這個(gè)指針就存在我們?cè)谇懊嫠吹降膒Entry-pCF中。 6: STDMETHOD(QueryInterface)(REFIID iid, void * ppvObject)return _InternalQueryInterface(iid, ppvObject); 現(xiàn)在調(diào)用的是CComObjectCached:QueryInterface,至于這個(gè)類有何特別之處,我們現(xiàn)在好象還不需要知道,我也很累的說(shuō),呵呵。 7: HRESULT _InternalQueryInterface(REFIID iid, void* ppvObject) return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); 所有的類的_InternalQueryInterface(.)都是在BEGIN_COM_MAP中定義的。 CComObjectCached沒有BEGIN_COM_MAP宏,所以現(xiàn)在調(diào)用的是CComClassFactory的。注意把this指針和接口映射數(shù)組_GetEntries()傳給了InternalQueryInterface(), 這是InternalQueryInterface(.)實(shí)現(xiàn)查詢的依據(jù)。在BEGIN_COM_MAP(x)中定義了一個(gè)靜態(tài)的接口映射數(shù)組: _ATL_INTMAP_ENTRY _entries; 每一個(gè)接口映射宏實(shí)際上都是向這個(gè)數(shù)組中增加了一項(xiàng)。一個(gè)接口映射宏包括三個(gè)部分:接口的IID號(hào)、偏移值(大部分時(shí)候下)、需要執(zhí)行的函數(shù),對(duì)一般接口來(lái)說(shuō)不用執(zhí)行其他函數(shù)。_GetEntries()就是返回這個(gè)數(shù)組。還有一些細(xì)節(jié)問(wèn)題以后再說(shuō)。 8: static HRESULT WINAPI InternalQueryInterface(void* pThis,const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void* ppvObject) . HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);. 現(xiàn)在調(diào)用的是CComObjectRootBase:InternalQueryInterface(.) 9:現(xiàn)在我們終于到了QueryInterface的鼻祖了。AtlInternalQueryInterface(.)是整個(gè)查詢過(guò)程的終點(diǎn),它遍歷接口映射表,并根據(jù)每一項(xiàng)做出相應(yīng)的動(dòng)作。ATL中的消息映射宏有很多種,相應(yīng)的動(dòng)作也很多,但現(xiàn)在我們不管那些,現(xiàn)在我們要做的就是查到一個(gè)IUnknown接口,這很容易,我們甚至不需要遍歷接口映射表。 ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis, const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void* ppvObject) ATLASSERT(pEntries-pFunc = _ATL_SIMPLEMAPENTRY); if (ppvObject = NULL) return E_POINTER; *ppvObject = NULL; if (InlineIsEqualUnknown(iid) / use first interface IUnknown* pUnk = (IUnknown*)(int)pThis+pEntries-dw); pUnk-AddRef(); *ppvObject = pUnk; return S_OK; ./還有一大堆呢,但現(xiàn)在用不上,就節(jié)省點(diǎn)空間吧 這里有一個(gè)規(guī)定,接口映射表的第一個(gè)接口必須是_ATL_SIMPLEENTRY型的。至于為什么有這個(gè)要求,以及pThis+pEntries-dw是什么意思,我們以后再說(shuō)吧,那也是一堆問(wèn)題??傊?,我們現(xiàn)在如愿以償輕松的獲得了我們所需要的類廠對(duì)象以及IUnknown指針。 4:我差一點(diǎn)以為我們可以勝得返回到第一步了,但在ATL:AtlModuleGetClassObject 處卻又停了下來(lái),看看它的源碼,原來(lái)還要再通過(guò)我們剛獲得的IUnknown指針查詢 IClassFactory指針。又是一通相同的調(diào)用,從第6步到第9步一模一樣,我們將進(jìn)行相同的調(diào)用。但注意在第9步中,我們這回查的不再是IUnknown指針了,所以我們需要看看我剛才還沒列出的代碼,但這留到下一次函數(shù)堆棧再看吧 1:終于終于我們已經(jīng)完成了創(chuàng)建類廠對(duì)象的全部操作,現(xiàn)在我們要做的就是我們熟悉的調(diào)用類廠對(duì)象的CreateInstance(.)函數(shù)創(chuàng)建組件的過(guò)程了。正如我們所見到的,現(xiàn)在OLE開始調(diào)用CComClassFactory:CreateInstance()了,我們還沒忘記,在類廠對(duì)象中保留了創(chuàng)建組件用的CreateInstance()函數(shù), 這個(gè)過(guò)程已經(jīng)很明朗了。 2.不用再重復(fù)了吧,看第4步。 3.不用再重復(fù)了吧,看第4步。 4.如果繼續(xù)路由下去的話,我們的堆棧還可以很長(zhǎng),但這只是重復(fù)的枯躁的勞動(dòng)。我就不繼續(xù)走下去了,我也很累的說(shuō),唉。 函數(shù)調(diào)用堆棧二: 0:. 5.ATL:AtlInternalQueryInterface(.) 4.ATL:CComObjectRootBase:InternalQueryInterface(.) 3.CMyObject:_InternalQueryInterface(.) 2.ATL:CComObject:QueryInterface(.) 1.pUnk-QueryInterface(IID_IMyObject, (void *)&pMyObject);(客戶端) 解釋如下: 1.我們通過(guò)剛剛獲得的組件對(duì)象的IUnknown接口指針來(lái)查詢IMyObject指針,這才是我們真正需要的指針。 2.還記得我們說(shuō)過(guò)ATL真正創(chuàng)建的組件并不是CMyObject,而是CComObject,CComAggObject 或CComPolyObject,這里我們創(chuàng)建的是CComObject.所以理所當(dāng)然我們要調(diào)用 CComObject:QueryInterface(.),而確實(shí)CComObject也實(shí)現(xiàn)了這個(gè)函數(shù)。 STDMETHOD(QueryInterface)(REFIID iid, void * ppvObject) return _InternalQueryInterface(iid, ppvObject); 它只是簡(jiǎn)單地調(diào)用_InternalQueryInterface(.),我們也說(shuō)過(guò),只有類里面申明了BEGIN_COM_MAP宏才會(huì)有_InternalQueryInterface(.),所以現(xiàn)在執(zhí)行轉(zhuǎn)到了它的父類CMyObject中去,所以將調(diào)用CMyObject:_InterfaceQueryInterface(.) 3.以后的調(diào)用我們已經(jīng)很熟悉了,還用我再說(shuō)一遍嗎,呵呵 4.這個(gè)調(diào)用我們也很熟悉了,不用多說(shuō)了吧 5.現(xiàn)在我們將要查詢的是一個(gè)非IUnknown接口,所以我們來(lái)看看我們以前沒列出的代碼 ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis, const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void* ppvObject) /確保接口映射的第一項(xiàng)是個(gè)簡(jiǎn)單接口 /若是查詢IUnknown接口,執(zhí)行相應(yīng)的操作 /以下將遍歷接口映射表,試圖找到相應(yīng)的接口 while (pEntries-pFunc != NULL) BOOL bBlind = (pEntries-piid = NULL); if (bBlind | InlineIsEqualGUID(*(pEntries-piid), iid) /_ATL_SIMPLEMAPENTRY就表明是個(gè)簡(jiǎn)單接口 if (pEntries-pFunc = _ATL_SIMPLEMAPENTRY) /offset ATLASSERT(!bBlind); IUnknown* pUnk = (IUnknown*)(int)pThis+pEntries-dw); pUnk-AddRef(); *ppvObject = pUnk; return S_OK; else /如果不是一個(gè)簡(jiǎn)單接口,則需要執(zhí)行相應(yīng)的函數(shù) HRESULT hRes=pEntries-pFunc(pThis,iid,ppvObject,pEntries-dw); if (hRes = S_OK | (!bBlind & FAILED(hRes) return hRes; pEntries+; return E_NOINTERFACE; 函數(shù)的邏輯很清楚,只有兩點(diǎn)可能不太理解,一個(gè)是 (IUnknown*)(int)pThis+pEntries-dw)是什么意思,另一個(gè)是pEntries-pFunc到底 要干些什么事。前一個(gè)問(wèn)題將在講述COM_INTERFACE_ENTRY2中講述,后一個(gè)問(wèn)題將在以后講述不同類型的接口時(shí)分別解釋。飯總是要一口一口吃的嘛,呵呵。 現(xiàn)在我們只需關(guān)心一下我們的IMyObject是怎么被查找的。看一下它的宏我們把COM_INTERFACE_ENTRY(IMyObject)解開以后形式為: &_ATL_IIDOF(IMyObject), /得到IMyObject的IID值 offsetofclass(IMyObject, CMyObject), /定義偏移量 _ATL_SIMPLEMAPENTRY,/表明是個(gè)簡(jiǎn)單接口 同樣對(duì)于offsetofclass(IMyObject, CMyObject)我們也將留到下一次再講。根據(jù)這個(gè)結(jié)構(gòu),我們很容易就能獲得IMyObject接口指針。 0:OK,it is over.依次退棧返回。 其實(shí)這次查詢發(fā)生的過(guò)程在剛才的調(diào)用序列中也發(fā)生了,當(dāng)查詢IClassFactory接口時(shí)就有類似的過(guò)程,但還是把它單獨(dú)提了出來(lái),只為了看看典型的情形,呵呵。二、COM_INTERFACE_ENTRY2(x, x2) ATL中是以多重繼承的方式來(lái)實(shí)現(xiàn)組件的,但在繼承樹中如果有多個(gè)分支實(shí)現(xiàn)了同一個(gè)接口,當(dāng)查詢這個(gè)接口時(shí)就需要知道把哪個(gè)分支返回給它。這個(gè)宏就是干這個(gè)工作的通常這個(gè)宏是用于IDispatch接口。我們先來(lái)看看它的典型用法: class COuter : public IDispatchImpl,/IOuter1是一個(gè)雙接口public IDispatchImpl,/IOuter2也是一個(gè)雙接口public . public: COuter() . BEGIN_COM_MAP(COuter) COM_INTERFACE_ENTRY2(IDispatch, IOuter2) ,/將暴露IOuter2所繼承的路線 , COM_INTERFACE_ENTRY(IOuter1) COM_INTERFACE_ENTRY(IOuter2) . END_COM_MAP; IDispatchImpl這個(gè)類中實(shí)現(xiàn)了IDispatch接口,所以現(xiàn)在組件中有兩個(gè)IDispatch 的實(shí)現(xiàn)。那查詢IDispatch接口時(shí),返回哪個(gè)實(shí)現(xiàn)呢? 我們?cè)賮?lái)看看COM_INTERFACE_ENTRY2(x, x2)的定義 #define BEGIN_COM_MAP(x) public: typedef x _ComMapClass; . #define COM_INTERFACE_ENTRY2(x, x2) &_ATL_IIDOF(x), /得到接口的IID值 (DWORD)(x*)(x2*)(_ComMapClass*)8)-8, _ATL_SIMPLEMAPENTRY, /表明是一個(gè)簡(jiǎn)單接口 現(xiàn)在問(wèn)題就在于(DWORD)(x*)(x2*)(_ComMapClass*)8)-8是個(gè)什么意思? 我們先來(lái)考察一下下面一段代碼: class A1 public: virtual void Test() ; class A2 : public A1 public: virtual void Test() ; class A3 : public A1 public: virtual void Test() ; class A : public A2, public A3 ; DWORD dw; dw = (DWORD)(A *)8); /dw = 0x08 dw = (DWORD)(A3 *)(A *)8); /dw = 0x0c dw = (DWORD)(A1 *)(A3 *)(A *)8); /dw = 0x0c dw = (DWORD)(A1 *)(A3 *)(A *)8) - 8;/dw = 4 這個(gè)繼承圖是個(gè)典型的菱形結(jié)構(gòu),在類A中保存有兩個(gè)虛函數(shù)表指針,分別代表著它的兩個(gè)分支。當(dāng)為類A申明一個(gè)對(duì)象并實(shí)例化時(shí),系統(tǒng)會(huì)為其分配內(nèi)存。在這塊內(nèi)存的最頂端保留著它的兩個(gè)虛函數(shù)表指針。分析程序運(yùn)行的結(jié)果,可以看出,最后的結(jié)果4代表了指向接口A3的虛函數(shù)表指針與類A對(duì)象的內(nèi)存塊頂端之間的偏移量。 下面我們?cè)倏匆粋€(gè)更為復(fù)雜點(diǎn)的繼承關(guān)系: class B1 public: virtual void Test() ; class B2 public: virtual void Test() ; class B3 public: public: virtual void Test() ; class B4 : public B1, public B2 public: virtual void Test() ; class B5 : public B2, public B3 public: virtual void Test() ; class B : public B4, public B5 ; DWORD dw; dw = (DWORD)(B *)8); /dw = 0x08 dw = (DWORD)(B5 *)(B *)8);/dw = 0x10 dw = (DWORD)(B2 *)(B5 *)(B *)8);/dw = 0x10 dw = (DWORD)(B2 *)(B5 *)(B *)8) - 8;/dw = 8 類B將保留四個(gè)虛函數(shù)表指針,因?yàn)樗灿兴膫€(gè)分支。我們的目的是想獲得B:B5:B2這個(gè)分支中的B2接口,最后的結(jié)果8正是我們所需要的,它表示在類B內(nèi)存塊的偏移量。從上面兩個(gè)例子中,我們已經(jīng)明白了(DWORD)(x*)(x2*)(_ComMapClass*)8)-8的作用通過(guò)這個(gè)值我們能獲得我們所需要的接口。 下面我們針對(duì)我們的實(shí)際情況COM_INTERFACE_ENTRY2(IDispatch, IOuter2)來(lái)分析一下IDispatchImpl模板類從類T中派生,所以COuter要從兩個(gè)它的模板類中繼承, IOuter1、IOuter2都是雙接口,即都是從IDispatch派生的類,所以可得COuter有兩條分支,也是個(gè)菱形結(jié)構(gòu),所以按照我們的示例,這個(gè)偏移值也應(yīng)該是4。為了證明我們的設(shè)想,我們?cè)賮?lái)通過(guò)函數(shù)堆棧來(lái)驗(yàn)證我們的結(jié)果。 函數(shù)堆棧: 5.ATL:AtlInternalQueryInterface(.) 4.ATL:CComObjectRootBase:InternalQueryInterface(.) 3.CMyObject:_InternalQueryInterface(.) 2.ATL:CComObject:QueryInterface(.) 1.pUnk-QueryInterface(IID_IDispatch, (void *)&pDispatch) 解釋: 1:這是我們的驗(yàn)證代碼,pUnk是組件的IUnknown指針 2-5:這些代碼我們現(xiàn)在都已經(jīng)很熟悉了,我們只需再看看AtlInternalQueryInterface 的具體實(shí)現(xiàn)。 ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis, const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void* ppvObject) . while (pEntries-pFunc != NULL) BOOL bBlind = (pEntries-piid = NULL); if (bBlind | InlineIsEqualGUID(*(pEntries-piid), iid) if (pEntries-pFunc = _ATL_SIMPLEMAPENTRY) /offset ATLASSERT(!bBlind); IUnknown* pUnk = (IUnknown*)(int)pThis+pEntries-dw); pUnk-AddRef(); *ppvObject = pUnk; return S_OK; ./如果是非簡(jiǎn)單接口的話. pEntries+; return E_NOINTERFACE; 關(guān)鍵的一句話就是IUnknown* pUnk = (IUnknown*)(int)pThis+pEntries-dw); 通過(guò)觀察變量,正如我們所料pEntries-dw=4。(int)pThis+pEntries-dw)保證了我們可以得到IOuter2分支的虛函數(shù)表,又因?yàn)镮Dispatch也是從IUnknown繼承,在虛函數(shù)表的最頂端放的是IUnknown的虛函數(shù)指針,所以進(jìn)行(IUnknown *)強(qiáng)制轉(zhuǎn)換,可以獲得這個(gè)虛函數(shù)表的頂端地址,這正是我們所需要的。或許會(huì)問(wèn)為什么得到的是虛函數(shù)表的地址,而不是一個(gè)類實(shí)例的地址呢?別忘了,接口是沒有數(shù)據(jù)的,它只有純虛函數(shù)。對(duì)于客戶來(lái)說(shuō),它只能通過(guò)接口定義的虛函數(shù)來(lái)訪問(wèn)它,而不可能訪問(wèn)實(shí)現(xiàn)接口的類的成員變量,組件的數(shù)據(jù)對(duì)客戶來(lái)說(shuō)是不可見的,所以只用得到虛函數(shù)表的地址就行了。三、COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) 使用這個(gè)宏的目的就是為了把一些很少用到的接口放在一個(gè)單獨(dú)的組件中實(shí)現(xiàn),僅當(dāng)查詢到這個(gè)接口時(shí),才創(chuàng)建這個(gè)組件,并且當(dāng)它的引用計(jì)數(shù)減為0時(shí)就會(huì)被釋放掉。我們知道ATL中組件是通過(guò)多重繼承實(shí)現(xiàn)的,每繼承一個(gè)接口,在為它分配的內(nèi)存塊中就會(huì)多一個(gè)虛函數(shù)表指針,用這個(gè)宏就可以為每個(gè)組件的實(shí)例節(jié)省下這一個(gè)虛函數(shù)表指針來(lái)(一個(gè)指針4個(gè)字節(jié),好象也不多啊,呵呵)下面我們來(lái)看它的典型用法: class CTearOff1: /該類是專門用來(lái)實(shí)現(xiàn)分割接口ITearOff1的 public IDispatchImpl,public CComTearOffObjectBase /外部對(duì)象 public: CTearOff1() CTearOff1() BEGIN_COM_MAP(CTearOff1) COM_INT
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 道具服裝租賃合同協(xié)議
- 車外殼配件采購(gòu)合同協(xié)議
- 損失賠償協(xié)議書協(xié)議書
- 社會(huì)學(xué)與文化研究試題集萃
- 《課程教學(xué)導(dǎo)論》課件
- 專家顧問(wèn)聘用合同協(xié)議
- 旅游占地協(xié)議書
- 轉(zhuǎn)讓泡沫加工合同協(xié)議
- 退休人員用用工合同協(xié)議
- 日本休戰(zhàn)協(xié)議書
- 口腔科各項(xiàng)規(guī)章制度
- 傳染病的預(yù)防和醫(yī)院感染的防控
- 年加工2萬(wàn)噸再生鋁項(xiàng)目可行性研究報(bào)告建議書
- 第20課 《飛奪瀘定橋》說(shuō)課稿-2024-2025學(xué)年統(tǒng)編版語(yǔ)文(五四學(xué)制)六年級(jí)上冊(cè)
- 眼科手術(shù)的安全管理
- 保安公司戰(zhàn)略發(fā)展規(guī)劃
- 【MOOC】外國(guó)教育史-河南大學(xué) 中國(guó)大學(xué)慕課MOOC答案
- 抗腫瘤藥物管理工作組成員及職責(zé)
- 2024年遼寧省中考生物真題卷及答案解析
- 第47屆世界技能大賽江蘇省選拔賽計(jì)算機(jī)軟件測(cè)試項(xiàng)目技術(shù)工作文件
- 2024年湖南高考真題化學(xué)試題(解析版)
評(píng)論
0/150
提交評(píng)論