_ATL_OBJMAP_ENTRY Com中的Object Map是怎么建立起来的

转载 2012年03月30日 10:13:37

写Com组件,你会注意到OBJECT_ENTRY_AUTO(__uuidof(CCalc), CCCalc)这个宏.

 

这个宏是做什么用的呢? 我们把它展开慢慢来分析.

 

#define OBJECT_ENTRY_AUTO(clsid, class) /

        __declspec(selectany) ATL::_ATL_OBJMAP_ENTRY __objMap_##class = {&clsid, class::UpdateRegistry, class::_ClassFactoryCreatorClass::CreateInstance, class::_CreatorClass::CreateInstance, NULL, 0, class::GetObjectDescription, class::GetCategoryMap, class::ObjectMain }; /

        extern "C" __declspec(allocate("ATL$__m")) __declspec(selectany) ATL::_ATL_OBJMAP_ENTRY* const __pobjMap_##class = &__objMap_##class; /

        OBJECT_ENTRY_PRAGMA(class)

 

可以猜测这个是建立OBJECT ENTRY MAP的过程.

 

类似于MFC中消息映射到创建过程,此类MAP映射应该有这样的结果

 

BEGIN_MESSAGE_MAP()

       ON_WM_MESSAGE()

       ...

END_MESSAGE_MAP()

 

为啥,这里只有一句宏声明呢.

其实ATL7.0之前,OBJECT ENTRY MAP 的创建过程是类似于消息映射MAP的创建的.

 

BEGIN_OBJECT_MAP()

  OBJECT_ENTRY()

  ...

END_OBJECT_MAP()

 

但是7.0之后提供了更高级的实现方式.主要得益于编译器的支持啦.

 

在atlbase.h文件中,你可以看到2个全局变量声明

 

extern "C"

{

__declspec(selectany) __declspec(allocate("ATL$__a")) _ATL_OBJMAP_ENTRY* __pobjMapEntryFirst = NULL;

__declspec(selectany) __declspec(allocate("ATL$__z")) _ATL_OBJMAP_ENTRY* __pobjMapEntryLast = NULL;

}

 

另外在CAtlComModule的构造函数中你可以看到这两句代码

 

m_ppAutoObjMapFirst = &__pobjMapEntryFirst + 1;

m_ppAutoObjMapLast = &__pobjMapEntryLast;

 

也就是说,OBJECT ENTRY MAP 的第一个成员就是__pobjMapEntryFirst的下一个.最后一个成员就是__pobjMapEntryLast.

 

OK.到这里的话,大概走了一半了.

 

继续看一下DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) 是如何,去得对应的接口的.

 

跟一下,可以看到走到了这里

 

HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) throw()

{

T* pT = static_cast<T*>(this);

return pT->GetClassObject(rclsid, riid, ppv);

}

 

不得不贴代码了.代码对码男来说更直观.

inline HRESULT CComModule::GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) throw()

{

if (ppv == NULL)

return E_POINTER;

HRESULT hr = S_OK;

_ATL_OBJMAP_ENTRY* pEntry;

if (m_pObjMap != NULL)

{

pEntry = m_pObjMap;

while (pEntry->pclsid != NULL)

{

if ((pEntry->pfnGetClassObject != NULL) && InlineIsEqualGUID(rclsid, *pEntry->pclsid))

{

if (pEntry->pCF == NULL)

{

CComCritSecLock<CComCriticalSection> lock(_AtlComModule.m_csObjMap, false);

hr = lock.Lock();

if (FAILED(hr))

{

ATLTRACE(atlTraceCOM, 0, _T("ERROR : Unable to lock critical section in CComModule::GetClassObject/n"));

ATLASSERT(0);

break;

}

if (pEntry->pCF == NULL)

hr = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (LPVOID*)&pEntry->pCF);

}

if (pEntry->pCF != NULL)

hr = pEntry->pCF->QueryInterface(riid, ppv);

break;

}

pEntry++;

}

}

if (*ppv == NULL && hr == S_OK)

hr = AtlComModuleGetClassObject(&_AtlComModule, rclsid, riid, ppv);

return hr;

}

 

这个字段m_pObjMap即是OBJECT ENTRY MAP 的指针.

啥时候初始化的呢?

 

inline HRESULT CComModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE /*h*/, const GUID* plibid) throw()

{

if (plibid != NULL)

m_libid = *plibid;

 

_ATL_OBJMAP_ENTRY* pEntry;

if (p != (_ATL_OBJMAP_ENTRY*)-1)

{

m_pObjMap = p;

if (m_pObjMap != NULL)

{

pEntry = m_pObjMap;

while (pEntry->pclsid != NULL)

{

pEntry->pfnObjectMain(true); //initialize class resources

pEntry++;

}

}

}

for (_ATL_OBJMAP_ENTRY** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)

{

if (*ppEntry != NULL)

(*ppEntry)->pfnObjectMain(true); //initialize class resources

}

return S_OK;

}

 

调用此函数是在DllMain中.

_Module.Init(ObjectMap, hInstance, &LIBID_ATLLib);

 

_Module的创建对应于atl.cpp中

 

CComModule _Module;

 

BEGIN_OBJECT_MAP(ObjectMap)

OBJECT_ENTRY(CLSID_Registrar, CDLLRegObject)

OBJECT_ENTRY_NON_CREATEABLE(CAxHostWindow)

END_OBJECT_MAP()

 

而_AtlComModule.m_ppAutoObjMapFirst这个你应该知道是何时复制到了吧.在_AtlComModule构造的时候,已经被赋值了.

而_AtlComModule.m_ppAutoObjMapFirst到_AtlComModule.m_ppAutoObjMapLast之间的成员,即是我们所定义的Com类和CLSID之间的对应关系.

 

继续贴代码.

GetClassObject会调用 AtlComModuleGetClassObject.

 

ATLINLINE ATLAPI AtlComModuleGetClassObject(_ATL_COM_MODULE* pComModule, REFCLSID rclsid, REFIID riid, LPVOID* ppv)

{

ATLASSERT(pComModule != NULL);

if (pComModule == NULL)

return E_INVALIDARG;

if (pComModule->cbSize == 0)  // Module hasn't been initialized

return E_UNEXPECTED;

 

if (ppv == NULL)

return E_POINTER;

*ppv = NULL;

 

HRESULT hr = S_OK;

 

for (_ATL_OBJMAP_ENTRY** ppEntry = pComModule->m_ppAutoObjMapFirst; ppEntry < pComModule->m_ppAutoObjMapLast; ppEntry++)

{

if (*ppEntry != NULL)

{

_ATL_OBJMAP_ENTRY* pEntry = *ppEntry;

if ((pEntry->pfnGetClassObject != NULL) && InlineIsEqualGUID(rclsid, *pEntry->pclsid))

{

if (pEntry->pCF == NULL)

{

CComCritSecLock<CComCriticalSection> lock(pComModule->m_csObjMap, false);

hr = lock.Lock();

if (FAILED(hr))

{

ATLTRACE(atlTraceCOM, 0, _T("ERROR : Unable to lock critical section in AtlComModuleGetClassObject/n"));

ATLASSERT(0);

break;

}

if (pEntry->pCF == NULL)

hr = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (LPVOID*)&pEntry->pCF);

}

if (pEntry->pCF != NULL)

hr = pEntry->pCF->QueryInterface(riid, ppv);

break;

}

}

}

 

if (*ppv == NULL && hr == S_OK)

hr = CLASS_E_CLASSNOTAVAILABLE;

return hr;

}

 

可以看到里面就是比较OBJECT ENTRY MAP 之间中CLSID.

 

终于走完第2步了.

 

下面解释一下,为什么所有声明的OBJECT ENTRY 都是在一起的呢.

这主要利用了编译器的__declspec(allocate("ATL$__z"))这个特性

 

编译器会把所有声明的区域按照字母排序

__pobjMapEntryFirst 声明为ATL$__a

__pobjMapEntryLast 声明为ATL$__z

而自己定义的类声明为ATL$__m

所以排序你肯定可以知道,自己生命的类的OBJECT ENTRY 会排成一个数组.


模拟ATL建立Auto Object Map

  • 2009年02月11日 18:17
  • 5KB
  • 下载

将节点插入到建立起来的有序链表中

/* * 程序的版权和版本声明部分 * Copyright (c)2014, 烟台大学计算机学院学生 * All rightsreserved. * 文件名称: fibnacci.cpp *...

用ATL建立轻量级的COM对象

本文假设你熟悉C++和COM。 摘要:     ATL——活动模板库(The Active Template Library),其设计旨在让人们用C++方便灵活地开发COM对象。ATL本身相当小巧灵...

使用ATL建立支持IClassFactory2的COM组件

使用ATL建立支持IClassFactory2的COM组件 发布: 2007-7-01 20:40 | 作者: admin | 来源: | 查看: 19次 | 进入软件测试论坛讨论 领测软件测...
  • worldy
  • worldy
  • 2013年11月15日 17:11
  • 629

用ATL建立轻量级的COM对象(二)

起步篇 在本文的第一部分,我们简要介绍了ATL的一些背景知识以及ATL所面向的开发技术和环境。在这一部分 将开始走进ATL,讲述ATL编程的基本方法、原则和必须要注意的问题。 理解ATL最容易的方...

用ATL建立轻量级的COM对象(八)

第一部分:为什么要使用ATL。 第二部分:起步篇。 第三部分:实现IUnknown。 第四部分:实现接口。 第五部分:不要过分抽象。 第六部分:输出你的类。 第七部...
  • wishfly
  • wishfly
  • 2013年08月22日 16:24
  • 793

ATL建立Com的相关学习和编程总结

Com的相关概念学习,网上有很多资料,
  • zedyqer
  • zedyqer
  • 2014年08月05日 15:15
  • 309

用ATL建立轻量级的COM对象(七)

第一部分:为什么要使用ATL。 第二部分:起步篇。 第三部分:实现IUnknown。 第四部分:实现接口。 第五部分:不要过分抽象。 第六部分:输出你的类。 ATL和注册表 CComM...

用ATL建立轻量级的COM对象(六)

第一部分:为什么要使用ATL。 第二部分:起步篇。 第三部分:实现IUnknown。 第四部分:实现接口。 第五部分:不要过分抽象。 输出你的类 实现了 CComObject ,你就有足...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:_ATL_OBJMAP_ENTRY Com中的Object Map是怎么建立起来的
举报原因:
原因补充:

(最多只允许输入30个字)