使用嵌套類(lèi)來(lái)實(shí)現接口
例子:
class CDictionaryObj : public CCmdTarget
{
DECLARE_DYNCREATE(CDictionaryObj)
CDictionaryObj();
public:
class XDictionary : public IDictionary
{
public:
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
virtual HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppvObj);
virtual BOOL __stdcall Initialize();
......
} m_xDictionary;
friend class XDictionary;
protected:
virtual ~CDictionaryObj();
DECLARE_MESSAGE_MAP()
DECLARE_INTERFACE_MAP()
DECLARE_OLECREATE(CDictionaryObj)
};
效果和class CDictionaryObj : public IDictionary 一樣。因為對外部而言,使用QueryInterface來(lái)獲取接口指針,獲取IDictionary指針,相當于返回m_xDictionary的地址。然后就可以通過(guò)此指針來(lái)操作IDictionary提供的接口了。
上面的例子能夠看出CDictionaryObj 并沒(méi)有實(shí)現QueryInterface, AddRef, Release函數。這是因為他繼承于CCmdTarget,CCmdTarget已經(jīng)實(shí)現了。而且它的實(shí)現還可以調用到內部類(lèi)的三個(gè)接口函數。
CCmdTarget提供的消息機制中,使用了靜態(tài)映射表(消息ID和處理函數在WinMain執行之前,就已經(jīng)建立好對應關(guān)系了)。同樣,COM機制中使用接口映射表。
宏 | 代碼 |
DECLARE_INTERFACE_MAP() | private: static const AFX_INTERFACEMAP_ENTRY _interfaceEntries[]; protected: static AFX_DATA const AFX_INTERFACEMAP interfaceMap; static const AFX_INTERFACEMAP* PASCAL _GetBaseInterfaceMap(); virtual const AFX_INTERFACEMAP* GetInterfaceMap() const; |
struct AFX_INTERFACEMAP_ENTRY | struct AFX_INTERFACEMAP_ENTRY { const void* piid; // the interface id (IID) (NULL for aggregate) size_t nOffset; // offset of the interface vtable from m_unknown }; |
struct AFX_INTERFACEMAP | struct AFX_INTERFACEMAP { // NULL is root class const AFX_INTERFACEMAP* (PASCAL* pfnGetBaseMap)(); // map for this class const AFX_INTERFACEMAP_ENTRY* pEntry; }; |
BEGIN_INTERFACE_MAP(theClass, theBase) | const AFX_INTERFACEMAP* PASCAL theClass::_GetBaseInterfaceMap() { return &theBase::interfaceMap; } const AFX_INTERFACEMAP* theClass::GetInterfaceMap() const { return &theClass::interfaceMap; } AFX_COMDAT const AFX_DATADEF AFX_INTERFACEMAP theClass::interfaceMap = { &theClass::_GetBaseInterfaceMap, &theClass::_interfaceEntries[0], }; AFX_COMDAT const AFX_DATADEF AFX_INTERFACEMAP_ENTRY theClass::_interfaceEntries[] = { |
INTERFACE_PART(theClass, iid, localClass) | { &iid, offsetof(theClass, m_x##localClass) }, |
END_INTERFACE_MAP() | { NULL, (size_t)-1 } }; |
DECLARE_OLECREATE(class_name) | public: static AFX_DATA COleObjectFactory factory; static AFX_DATA const GUID guid; |
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)); AFX_COMDAT const AFX_DATADEF GUID class_name::guid = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; |
說(shuō)明: _interfaceEntries[] 保存了AFX_INTERFACEMAP_ENTRY 數組,這個(gè)數組也就是接口ID和接口vtable與父類(lèi)指針的偏移量的映射表。interfaceMap 其中一個(gè)指針指向父類(lèi),另外一個(gè)指向_interfaceEntries[]。_GetBaseInterfaceMap函數返回interfaceMap的地址。這張映射表的實(shí)現是通過(guò)BEGIN_INTERFACE_MAP(theClass, theBase), INTERFACE_PART(theClass, iid, localClass), END_INTERFACE_MAP() 三個(gè)宏實(shí)現的。
CCmdTarget 實(shí)現了倆個(gè)版本的IUnkown,非委托、委托IUnkown。
從CCmdTarget繼承下來(lái)的,為了實(shí)現聚合,調用的應該是委托IUnkown接口(External)。
目前為止,只有接口的實(shí)現,沒(méi)有出現類(lèi)廠(chǎng),類(lèi)廠(chǎng)已經(jīng)被MFC自動(dòng)生成了。
在DLL 的AppWizard向導中選中:Automation,向導會(huì )生成如下代碼:
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return AfxDllGetClassObject(rclsid, riid, ppv);
}
STDAPI DllCanUnloadNow(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return AfxDllCanUnloadNow();
}
// by exporting DllRegisterServer, you can use regsvr.exe
STDAPI DllRegisterServer(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
COleObjectFactory::UpdateRegistryAll();
return S_OK;
}
我們知道,類(lèi)廠(chǎng)應該是DllGetClassObject里面創(chuàng )建的,這個(gè)函數調用了AfxDllGetClassObject。秘密就在其后。
SCODE AFXAPI AfxDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
*ppv = NULL;
DWORD lData1 = rclsid.Data1;
// search factories defined in the application
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AfxLockGlobals(CRIT_OBJECTFACTORYLIST);
for (COleObjectFactory* pFactory = pModuleState->m_factoryList;
pFactory != NULL; pFactory = pFactory->m_pNextFactory)
{
if (pFactory->m_bRegistered != 0 &&
lData1 == pFactory->m_clsid.Data1 &&
((DWORD*)&rclsid)[1] == ((DWORD*)&pFactory->m_clsid)[1] &&
((DWORD*)&rclsid)[2] == ((DWORD*)&pFactory->m_clsid)[2] &&
((DWORD*)&rclsid)[3] == ((DWORD*)&pFactory->m_clsid)[3])
{
// found suitable class factory -- query for correct interface
SCODE sc = pFactory->InternalQueryInterface(&riid, ppv);
AfxUnlockGlobals(CRIT_OBJECTFACTORYLIST);
return sc;
}
}
AfxUnlockGlobals(CRIT_OBJECTFACTORYLIST);
#ifdef _AFXDLL
AfxLockGlobals(CRIT_DYNLINKLIST);
// search factories defined in extension DLLs
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL;
pDLL = pDLL->m_pNextDLL)
{
for (pFactory = pDLL->m_factoryList;
pFactory != NULL; pFactory = pFactory->m_pNextFactory)
{
if (pFactory->m_bRegistered != 0 &&
lData1 == pFactory->m_clsid.Data1 &&
((DWORD*)&rclsid)[1] == ((DWORD*)&pFactory->m_clsid)[1] &&
((DWORD*)&rclsid)[2] == ((DWORD*)&pFactory->m_clsid)[2] &&
((DWORD*)&rclsid)[3] == ((DWORD*)&pFactory->m_clsid)[3])
{
// found suitable class factory -- query for correct interface
SCODE sc = pFactory->InternalQueryInterface(&riid, ppv);
AfxUnlockGlobals(CRIT_DYNLINKLIST);
return sc;
}
}
}
AfxUnlockGlobals(CRIT_DYNLINKLIST);
#endif
// factory not registered -- return error
return CLASS_E_CLASSNOTAVAILABLE;
}
MFC使用的就是COleObjectFactory 這個(gè)通用的類(lèi)廠(chǎng)(Ole是歷史原因導致的,其實(shí)CComObjectFactory更加合適)。
COleObjectFactory 從 CCmdTarget派生,實(shí)現了IClassFactory2接口。此類(lèi)廠(chǎng)使用MFC動(dòng)態(tài)創(chuàng )建機制(DECLARE_DYNCREATE),創(chuàng )建對象。那么如何將此類(lèi)廠(chǎng)和某個(gè)類(lèi)關(guān)聯(lián)?使用DECLARE_OLECREATE,IMPLEMENT_OLECREATE。他們將類(lèi)的CLSID和類(lèi)的RUNTIME信息傳遞給CComObjectFactory類(lèi)廠(chǎng)。這樣類(lèi)廠(chǎng)就能使用CLSID和動(dòng)態(tài)信息進(jìn)行創(chuàng )建對象了。那么類(lèi)廠(chǎng)是如何被Afx找到的呢。從代碼中看出,類(lèi)廠(chǎng)指針的是從模塊信息里面的類(lèi)廠(chǎng)鏈來(lái)獲取的。而類(lèi)廠(chǎng)構造函數中調用了pModuleState->m_factoryList.AddHead(this); 將自己的信息加入模塊信息里面,模塊信息是全局的變量。這樣就跑通了。流程如下:
AfxDllGetClassObject ->
Module State (factoryList), look up Factory’s clsid. ->
Get IClassFactory2 reference ->
CreateInstance ->
Dynamic create object using Dynamic information. ->
Finish.
What should we do?
VC++ 6.0 提供了MFC和ATL兩套庫,用來(lái)開(kāi)發(fā)COM。
這里使用MFC:
1. 使用AppWizard生產(chǎn)MFC exe/dll, 選擇Automation
2. 定義接口
3. 繼承CCmdTarget,實(shí)現接口,(嵌入式)BEGIN_INTERFACE_PART, INIT_INTERFACE_PART, STDMETHOD, STDMETHOD_, END_INTERFACE_PART.
4. 定義各個(gè)接口函數體:Class::XlocalClass::AddRef/Release/QueryInterface(), 實(shí)現接口的各個(gè)接口
5. 定義類(lèi)的靜態(tài)信息,DECLARE_INTERFACE_MAP,實(shí)現使用:BEGIN_INTERFACE_MAP, INTERFACE_PART, END_INTERFACE_MAP
6. 使用MFC類(lèi)廠(chǎng),類(lèi)里面使用:DECLARE_OLECREATE, 實(shí)現使用 IMPLEMENT_OLECREATE聯(lián)系客服