1,offsetofclass
获取基类相对于子类的偏移位置。

#define _ATL_PACKING 8
#define
offsetofclass(base, derived) ((DWORD_PTR)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)

_ATL_PACKING非零就行,只是作为一个地址。因为为了避免虚类无法创建对象的问题所以没有通过类对象来计算。

2,
//If you get a message that FinalConstruct is ambiguous then you need to
// override it in your class and call each base class' version of this
#define
BEGIN_COM_MAP(x) public: \
    typedef
x
_ComMapClass; \
    IUnknown* _GetRawUnknown() throw() \
    { ATLASSERT(_GetEntries()[0].pFunc == _ATL_SIMPLEMAPENTRY); return (IUnknown*)((INT_PTR)this+_GetEntries()->dw); }
\
    _ATL_DECLARE_GET_UNKNOWN(x)\
    HRESULT
_InternalQueryInterface(REFIID
iid, void** ppvObject) throw() \
    { return
InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } \
    const
static
ATL::_ATL_INTMAP_ENTRY* WINAPI
_GetEntries() throw() { \
    static
const
ATL::_ATL_INTMAP_ENTRY
_entries[]
= { DEBUG_QI_ENTRY(x)

struct
_ATL_INTMAP_ENTRY
{
    const
IID* piid; // the interface id (IID)
    DWORD_PTR
dw;
    _ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr
};

#define
DEBUG_QI_ENTRY(x) \
        {NULL, \
        (DWORD_PTR)_T(#x), \
        (ATL::_ATL_CREATORARGFUNC*)0},

typedef
HRESULT (WINAPI
_ATL_CREATORARGFUNC)(void* pv, REFIID
riid, LPVOID* ppv, DWORD_PTR
dw);

#define
COM_INTERFACE_ENTRY(x)\
    {&_ATL_IIDOF(x), \
    offsetofclass(x, _ComMapClass), \
    _ATL_SIMPLEMAPENTRY},

#define
_ATL_IIDOF(x) __uuidof(x)
//__uuidof获取与x相关的GUID值

#define
_ATL_SIMPLEMAPENTRY ((ATL::_ATL_CREATORARGFUNC*)1)

#define
END_COM_MAP() \
    __if_exists(_GetAttrEntries) {{NULL, (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }\
    {NULL, 0, 0}}; return &_entries[1];} \
    virtual
ULONG
STDMETHODCALLTYPE
AddRef( void) throw() = 0; \
    virtual
ULONG
STDMETHODCALLTYPE
Release( void) throw() = 0; \
    STDMETHOD(QueryInterface)(REFIID, void**) throw() = 0;

3,
static
HRESULT
WINAPI CComObjectRootBase ::InternalQueryInterface(void* pThis,
        const
_ATL_INTMAP_ENTRY* pEntries, REFIID
iid, void** ppvObject)
    {
        ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
        HRESULT
hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
        return
_ATLDUMPIID(iid, pszClassName, hRes);
    }

// QI support
ATLINLINE ATLAPI AtlInternalQueryInterface(
    _Inout_ void* pThis,
    _In_ const _ATL_INTMAP_ENTRY* pEntries,
    _In_ REFIID iid,
    _COM_Outptr_ void** ppvObject)
{
    // First entry in the com map should be a simple map entry
    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);

    if (InlineIsEqualUnknown(iid)) // use first interface
    {
        IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
        pUnk->AddRef();
        *ppvObject = pUnk;
        return S_OK;
    }

    HRESULT hRes;
    for (;; pEntries++)
    {
        if (pEntries->pFunc == NULL)
        {
            hRes = E_NOINTERFACE;
            break;
        }

        BOOL bBlind = (pEntries->piid == NULL);
        if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
        {
            if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
            {
                ATLASSERT(!bBlind);
                IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
                pUnk->AddRef();
                *ppvObject = pUnk;
                return S_OK;
            }

            // Actual function call

            hRes = pEntries->pFunc(pThis,
                iid, ppvObject, pEntries->dw);
            if (hRes == S_OK)
                return S_OK;
            if (!bBlind && FAILED(hRes))
                break;
        }
    }

    *ppvObject = NULL;
    return hRes;
}
CComObjectRootBase的InternalQueryInterface内部调用AtlInternalQueryInterface遍历_ATL_INTMAP_ENTRY数组(包含所有实现接口的映射表),找到基类的虚函数表返回。
(我原来用的VS2005,没有AtlInternalQueryInterface的实现,蛋疼了好久这个接口映射表的怎么返回的,我猜也是遍历但是没有看见源码一直心头惴惴不安。后面擦查看VS2013里面给出了AtlInternalQueryInterface的实现才把这口气舒出来。)

4,
ATL提供了BEGIN_COM_MAP、END_COM_MAP、COM_INTERFACE_ENTRY与COM_INTERFACE_ENTRY2这4个宏来创建接口映射表。
假设一个类CClassA继承了接口IIntA1和IIntA2,则该类的接口映射表创建如下:

class CClassA : public CComObjectRootEx<CComSingleThreadMode>
{
    BEGIN_COM_MAP(CClassA)
        COM_INTERFACE_ENTRY(IIntA1)
        COM_INTERFACE_ENTRY(IIntA2)
    END_COM_MAP()
    ......
};

static
const
ATL::_ATL_INTMAP_ENTRY
_entries[] =
{
{NULL,     (DWORD_PTR)_T(#x),     (ATL::_ATL_CREATORARGFUNC*)0},
{& __uuidof(IIntA1), offsetofclass(IIntA1,
CClassA), (ATL::_ATL_CREATORARGFUNC*)1},
{& __uuidof(IIntA2), offsetofclass(IIntA2,
CClassA), (ATL::_ATL_CREATORARGFUNC*)1},
{NULL, 0, 0}
};

而当CClassB继承了IIntB1和IIntB2,并且IIntB1和IIntB2都继承自IDispatch接口。
此时,如果客户程序在查询IDispatch接口,QueryInterface所返回的IDispatch接口指针将无法确定其属于IIntB1还是IIntB2。
在这种情况下,需要指定IDispatch接口指针的默认指向。 COM_INTERFACE_ENTRY2()宏即是用于完成该功能。
下面代码将对IDispatch接口的请求默认指向属于IIntB2的IDispatch接口指针。
class CClassB : public CComObjectRootEx<CComSingleThreadMode>
{
    BEGIN_COM_MAP(CClassB)
        COM_INTERFACE_ENTRY(IIntB1)
        COM_INTERFACE_ENTRY(IIntB2)
        COM_INTERFACE_ENTRY2(IDispatch, IIntB2)
    END_COM_MAP()
    ......
};

#define COM_INTERFACE_ENTRY2(x, x2)\
    {&_ATL_IIDOF(x),\
    reinterpret_cast<DWORD_PTR>(static_cast<x*>(static_cast<x2*>(reinterpret_cast<_ComMapClass*>(8))))-8,\
    _ATL_SIMPLEMAPENTRY},

5,总结:
BEGIN_COM_MAP通过一个静态的_GetEntries()方法,来获取在该方法中创建的一个静态COM接口映射表。

Atl笔记二:BEGIN_COM_MAP的更多相关文章

  1. 《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX

    <CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...

  2. jQuery源码笔记(二):定义了一些变量和函数 jQuery = function(){}

    笔记(二)也分为三部分: 一. 介绍: 注释说明:v2.0.3版本.Sizzle选择器.MIT软件许可注释中的#的信息索引.查询地址(英文版)匿名函数自执行:window参数及undefined参数意 ...

  3. Mastering Web Application Development with AngularJS 读书笔记(二)

    第一章笔记 (二) 一.scopes的层级和事件系统(the eventing system) 在层级中管理的scopes可以被用做事件总线.AngularJS 允许我们去传播已经命名的事件用一种有效 ...

  4. Python 学习笔记二

    笔记二 :print 以及基本文件操作 笔记一已取消置顶链接地址 http://www.cnblogs.com/dzzy/p/5140899.html 暑假只是快速过了一遍python ,现在起开始仔 ...

  5. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  6. webpy使用笔记(二) session/sessionid的使用

    webpy使用笔记(二) session的使用 webpy使用系列之session的使用,虽然工作中使用的是django,但是自己并不喜欢那种大而全的东西~什么都给你准备好了,自己好像一个机器人一样赶 ...

  7. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  8. 《MFC游戏开发》笔记二 建立工程、调整窗口

    本系列文章由七十一雾央编写,转载请注明出处.  http://blog.csdn.net/u011371356/article/details/9300383 作者:七十一雾央 新浪微博:http:/ ...

  9. JavaScript基础笔记二

    一.函数返回值1.什么是函数返回值    函数的执行结果2. 可以没有return // 没有return或者return后面为空则会返回undefined3.一个函数应该只返回一种类型的值 二.可变 ...

随机推荐

  1. 也谈读书和书籍选择问题(C#)

    前言 读到一篇.net程序员应该看什么书?深有感触.以前曾经用C#也开发过几年的东西.在那里对相关语言和开发都有了一定的了解.这里,结合自己当初的一些体会和见识把一些比较好的书籍也和大家分享一下.这一 ...

  2. C#中使用GUID的笔记

    GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可 ...

  3. Eclipse 环境下安装PhoneGap开发插件

    phoneGap开发跨所有移动平台软件已经成为未来移动终端开发的总趋势,如何在大家所熟悉的Eclipse IDE中快速安装PhoneGap开发插件,介绍如下: 点击help——>install ...

  4. python源码解析

    http://blog.csdn.net/balabalamerobert/article/category/168910

  5. C++之运算符重载(2)

    上一节主要讲解了C++里运算符重载函数,在看了单目运算符(++)重载的示例后,也许有些朋友会问这样的问题.++自增运算符在C或C++中既可以放在操作数之前,也可以放在操作数之后,但是前置和后置的作用又 ...

  6. 高级I/O之STREAMS

    http://en.wikipedia.org/wiki/STREAMS STREAMS(流)是系统V提供的构造内核设备驱动程序和网络协议包的一种通用方法,对STREAMS进行讨论的目的是为了理解系统 ...

  7. 1043. Is It a Binary Search Tree (25)

    the problem is from pat,which website is http://pat.zju.edu.cn/contests/pat-a-practise/1043 and the ...

  8. QT的三种协议说明

    关于Qt的三种协议以及是否收费,有以下引文: 引文一: 最近一直在学习 Qt.Qt 有两个许可证:LGPL 和商业协议.这两个协议在现在的 Qt 版本中的代码是完全一致的(潜在含义是,Qt 的早期版本 ...

  9. iOS如何随意的穿插跳跃,push来pop去

    iOS如何随意的穿插跳跃,push来pop去? 主题思想:如A.B.C.D 四个视图控制器. 想要在 A push B 后, B 在push 到 D ,然后从 D pop 到 C ,在从 C pop ...

  10. IIS 发布 之 Word导出本地测试正常,发布报错

    用C#动态生成Word文档功能实现了,在本地的机器运行时是好的,但程序发布 IIS 或 远程服务器 上就报错, 报错信息为:检索 COM 类工厂中 CLSID 为 {000209FF-0000-000 ...