程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 向CCmdTarget的派生類添加一個接口的實現

向CCmdTarget的派生類添加一個接口的實現

編輯:vc教程

  向一個類中添加某個接口的實現,這是很常見的需求,特別是用在事件通知、連接點中更是多見。MFC類庫內的很多類也都有這樣的需求,比如類COleControl就實現了很多的接口。MFC自己實現的方法都用的是嵌套類,並且定義了幾個宏來簡化該過程。用同樣的方法,我們也可以很方便的在自己的類中添加一個接口的實現。CCmdTarget中實現了接口IDispatch,以及IUnknown 的三個函數的缺省實現。一般的MFC類都會從CCmdTarget繼承,所以這裡講的是典型的向CCmdTarget的派生類添加接口的方法。

  比如,有一個類CSampleView從CVIEw中繼承。現在要給它添加一個新的接口IMyTest,該接口只有一個空的方法Test()。添加過程如下:

(1)CSampleVIEw類定義中加入以下代碼:

    DECLARE_INTERFACE_MAP() //聲明接口映射
    BEGIN_INTERFACE_PART(TestInterface, IMyTest) //聲明實現接口IMyTest的嵌套類
        STDMETHOD(Test)();
    END_INTERFACE_PART(FontNotify2)


(2)CSampleVIEw類實現中加入以下代碼:

    BEGIN_INTERFACE_MAP(CSampleVIEw, CCmdTarget)
        INTERFACE_PART(CSampleVIEw, IID_IMyTest, TestInterface)
    END_INTERFACE_MAP()

    STDMETHODIMP_(ULONG) CSampleVIEw::XTestInterface::AddRef( )
    {
        METHOD_PROLOGUE_EX(CSampleVIEw, TestInterface)
        return (ULONG)pThis->ExternalAddRef();
    }

    STDMETHODIMP_(ULONG) CSampleVIEw::XTestInterface::Release( )
    {
        METHOD_PROLOGUE_EX(CSampleVIEw, TestInterface)
        return (ULONG)pThis->ExternalRelease();
    }

    STDMETHODIMP CSampleVIEw::XTestInterface::QueryInterface( REFIID iid, LPVOID FAR* ppvObj )
    {
        METHOD_PROLOGUE_EX(CSampleVIEw, TestInterface)
        return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj) ;
    }

    STDMETHODIMP CSampleVIEw::XTestInterface::Test()
    {
        METHOD_PROLOGUE_EX(CSampleVIEw, TestInterface)
        // do something you like
        return S_OK ;
    }
 
  揭開宏的神秘面紗,看看它到底是什麼東西。以下都是簡化的版本。

(1)DECLARE_INTERFACE_MAP

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
{
    const AFX_INTERFACEMAP* (PASCAL* pfnGetBaseMap)(); // NULL is root class
    const AFX_INTERFACEMAP_ENTRY* pEntry; // map for this class
};

#define 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;
 

(2)BEGIN_INTERFACE_PART/END_INTERFACE_PART

#define BEGIN_INTERFACE_PART(localClass, baseClass) // 定義了一個嵌套類
    class X##localClass : public baseClass
    {
    public:
        STDMETHOD_(ULONG, AddRef)();
        STDMETHOD_(ULONG, Release)();
        STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj);

#define END_INTERFACE_PART(localClass)
    } m_x##localClass;
    frIEnd class X##localClass;
 

(3)BEGIN_INTERFACE_MAP/INTERFACE_PART/END_INTERFACE_MAP

#define offsetof(s,m) (size_t)&(((s *)0)->m)

#define 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[] =
    {

#define INTERFACE_PART(theClass, iid, localClass)
        { &iid, offsetof(theClass, m_x##localClass) },

#define END_INTERFACE_MAP()
        { NULL, (size_t)-1 }
    };

(4)METHOD_PROLOGUE_EX

#define METHOD_PROLOGUE_EX(theClass, localClass)
    METHOD_PROLOGUE(theClass, localClass)

#define METHOD_PROLOGUE(theClass, localClass)
    theClass* pThis =
        ((theClass*)((BYTE*)this - offsetof(theClass, m_x##localClass)));
    AFX_MANAGE_STATE(pThis->m_pModuleState)
    pThis; // avoid warning from compiler

  METHOD_PROLOGUE最大的作用就是得到pThis指針。該宏用在嵌套類的成員函數中,pThis是其父類的指針,這裡也即是CSampleVIEw的this指針。

  這些宏與MFC中的消息映射宏非常的相似,在侯捷的《深入淺出MFC》中對消息映射宏有非常詳細的講述。我也無意畫蛇添足。它的基本思想就是把各個嵌套類的對象(即m_x開頭的變量)放到一個數組裡,這樣在QueryInterface時就可以得到這些接口的指針了,所謂的接口指針也就是這些嵌套類對象的地址。

  CCmdTarget包含了三個函數:ExternalAddRef、ExternalRelease、ExternalQueryInterface。這樣我們就不用自己實現IUnknown接口了,只要簡單地調用父類的函數就可以了,這實在是很方便。ExternalQueryInterface的執行過程就是先在子類中找要查詢的接口,如果找到了就返回其接口指針。如果找不到就通過GetBaseInterfaceMap到父類中去找,以此類推。跟消息映射的處理方法是一樣的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved