Atl笔记二:BEGIN_COM_MAP
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的更多相关文章
- 《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX
<CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...
- jQuery源码笔记(二):定义了一些变量和函数 jQuery = function(){}
笔记(二)也分为三部分: 一. 介绍: 注释说明:v2.0.3版本.Sizzle选择器.MIT软件许可注释中的#的信息索引.查询地址(英文版)匿名函数自执行:window参数及undefined参数意 ...
- Mastering Web Application Development with AngularJS 读书笔记(二)
第一章笔记 (二) 一.scopes的层级和事件系统(the eventing system) 在层级中管理的scopes可以被用做事件总线.AngularJS 允许我们去传播已经命名的事件用一种有效 ...
- Python 学习笔记二
笔记二 :print 以及基本文件操作 笔记一已取消置顶链接地址 http://www.cnblogs.com/dzzy/p/5140899.html 暑假只是快速过了一遍python ,现在起开始仔 ...
- WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
- webpy使用笔记(二) session/sessionid的使用
webpy使用笔记(二) session的使用 webpy使用系列之session的使用,虽然工作中使用的是django,但是自己并不喜欢那种大而全的东西~什么都给你准备好了,自己好像一个机器人一样赶 ...
- AJax 学习笔记二(onreadystatechange的作用)
AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...
- 《MFC游戏开发》笔记二 建立工程、调整窗口
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9300383 作者:七十一雾央 新浪微博:http:/ ...
- JavaScript基础笔记二
一.函数返回值1.什么是函数返回值 函数的执行结果2. 可以没有return // 没有return或者return后面为空则会返回undefined3.一个函数应该只返回一种类型的值 二.可变 ...
随机推荐
- 【javaSE】HashSet和HashMap
************************************************************************ ****原文:blog.csdn.net/clar ...
- ActionScript 3 中的强制类型转换
以前AS中是这样进行强制类型转换的:假设有一个类叫做Class1,我们声明了一个它的对象 c1,如果想要将它转换成Class2类型,只要这样写: Class2(c1); 在AS3中你依然可以这样写,但 ...
- C# Tcp协议收发数据
运行这个程序前需要先关闭Windows防火墙,Win7系统关闭防火墙的方法是在控制面板的“控制面板\系统和安全\Windows 防火墙\自定义设置”路径中,将“家庭或工作(专用)网络位置设置”和“公用 ...
- Branch and Bound:分支限界算法
http://blog.sciencenet.cn/blog-509534-728984.html 分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法.但与 ...
- spring源码分析之spring-jms模块详解
0 概述 spring提供了一个jms集成框架,这个框架如spring 集成jdbc api一样,简化了jms api的使用. jms可以简单的分成两个功能区,消息的生产和消息的消费.JmsTempl ...
- MySQL的数据库引擎的类型及区别
MySQL的数据库引擎的类型 你能用的数据库引擎取决于mysql在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和 ...
- cocos2d-x使用ant打包
1. 下载apache-ant-1.9.3,然后添加环境变量ANT_HOME = D:\dev_envir\apache-ant-1.9.3(你自己的ant根目录),再在path中添加路径:%ANT_ ...
- Android 高级UI设计笔记05:使用TextView实现跑马灯的效果
1. 使用TextView属性实现跑马灯的效果: (1). 新建一个Android工程,命名为"MarqueeTextViewDemo",如下: (2). 来到activity_m ...
- Python 中使用optparse进行参数解析
使用过Linux/Unix的人都会知道,命令行下的很多命令都需要参数,在C语言中添加和解析参数比较繁琐.Python中提供了optparse模块可以非常方便地处理命令行参数. 1 命令行参数的样 ...
- Extjs之success、failure
Ext.form.Action.Submit的配置选项success.failure是根据返回json中success属性判断的,如果success为true,则success,false则failu ...