com組件開發

使用嵌套類來實現接口 

例子:

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函數。這是因爲他繼承於CCmdTargetCCmdTarget已經實現了。而且它的實現還可以調用到內部類的三個接口函數。

 

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_OLECREATEIMPLEMENT_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 提供了MFCATL兩套庫,用來開發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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章