Chinaunix首页 | 论坛 | 博客
  • 博客访问: 14497470
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 20:54:30

下载本文示例代码
  向一个类中添加某个接口的实现,这是很常见的需求,特别是用在事件通知、连接点中更是多见。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到父类中去找,以此类推。跟消息映射的处理方法是一样的。感兴趣的话就自己去看下源码吧。   基本上已经清楚了,实际上知道怎么用就可以了,对其内幕稍微了解一下也就可以了。宏的功能实在是强大,我觉得这也是C 的魅力之一。不知道为什么有人反对使用宏,不要跟我讲一通宏的种种缺点,那不是宏的问题,而只是你自己的问题。:)   向一个类中添加某个接口的实现,这是很常见的需求,特别是用在事件通知、连接点中更是多见。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到父类中去找,以此类推。跟消息映射的处理方法是一样的。感兴趣的话就自己去看下源码吧。   基本上已经清楚了,实际上知道怎么用就可以了,对其内幕稍微了解一下也就可以了。宏的功能实在是强大,我觉得这也是C 的魅力之一。不知道为什么有人反对使用宏,不要跟我讲一通宏的种种缺点,那不是宏的问题,而只是你自己的问题。:) 下载本文示例代码


向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现向CCmdTarget的派生类添加一个接口的实现
阅读(138) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~