使用嵌套類來實現接口
例子:
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來獲取接口指針,獲取IDictionary指針,相當於返回m_xDictionary的地址。然後就可以通過此指針來操作IDictionary提供的接口了。
上面的例子能夠看出CDictionaryObj 並沒有實現QueryInterface, AddRef, Release函數。這是因爲他繼承於CCmdTarget,CCmdTarget已經實現了。而且它的實現還可以調用到內部類的三個接口函數。
CCmdTarget提供的消息機制中,使用了靜態映射表(消息ID和處理函數在WinMain執行之前,就已經建立好對應關係了)。同樣,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 } }; |
說明: _interfaceEntries[] 保存了AFX_INTERFACEMAP_ENTRY 數組,這個數組也就是接口ID和接口vtable與父類指針的偏移量的映射表。interfaceMap 其中一個指針指向父類,另外一個指向_interfaceEntries[]。_GetBaseInterfaceMap函數返回interfaceMap的地址。這張映射表的實現是通過BEGIN_INTERFACE_MAP(theClass, theBase), INTERFACE_PART(theClass, iid, localClass), END_INTERFACE_MAP() 三個宏實現的。
CCmdTarget 實現了倆個版本的IUnkown,非委託、委託IUnkown。
從CCmdTarget繼承下來的,爲了實現聚合,調用的應該是委託IUnkown接口(External)。
目前爲止,只有接口的實現,沒有出現類廠,類廠已經被MFC自動生成了。
在DLL 的AppWizard嚮導中選中:Automation,嚮導會生成如下代碼:
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;
}
我們知道,類廠應該是DllGetClassObject裏面創建的,這個函數調用了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 這個通用的類廠(Ole是歷史原因導致的,其實CComObjectFactory更加合適)。
COleObjectFactory 從 CCmdTarget派生,實現了IClassFactory2接口。此類廠使用MFC動態創建機制(DECLARE_DYNCREATE),創建對象。那麼如何將此類廠和某個類關聯?使用DECLARE_OLECREATE,IMPLEMENT_OLECREATE。他們將類的CLSID和類的RUNTIME信息傳遞給CComObjectFactory類廠。這樣類廠就能使用CLSID和動態信息進行創建對象了。那麼類廠是如何被Afx找到的呢。從代碼中看出,類廠指針的是從模塊信息裏面的類廠鏈來獲取的。而類廠構造函數中調用了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兩套庫,用來開發COM。
這裏使用MFC:
1. 使用AppWizard生產MFC exe/dll, 選擇Automation
2. 定義接口
3. 繼承CCmdTarget,實現接口,(嵌入式)BEGIN_INTERFACE_PART, INIT_INTERFACE_PART, STDMETHOD, STDMETHOD_, END_INTERFACE_PART.
4. 定義各個接口函數體:Class::XlocalClass::AddRef/Release/QueryInterface(), 實現接口的各個接口
5. 定義類的靜態信息,DECLARE_INTERFACE_MAP,實現使用:BEGIN_INTERFACE_MAP, INTERFACE_PART, END_INTERFACE_MAP
6. 使用MFC類廠,類裏面使用:DECLARE_OLECREATE, 實現使用 IMPLEMENT_OLECREATE