_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_OBJMAP_ENTRY Com中的Object Map是怎么建立起来的

写Com组件,你会注意到OBJECT_ENTRY_AUTO(__uuidof(CCalc), CCCalc)这个宏. 这个宏是做什么用的呢? 我们把它展开慢慢来分析. #define OBJECT_E...
  • deppcyan
  • deppcyan
  • 2010年07月15日 18:49
  • 1985

List<Map<String,Object>>取值知识点

List>的结果集怎么取值   A  取 String, Object示例代码(KeySet遍历): Map testMap = new HashMap(); testMap.put("1", ...
  • renzhe333
  • renzhe333
  • 2016年06月19日 20:39
  • 3328

ContextMap详解

ContextMap详解 基本介绍      ContextMap是OGNL的上下文对象,其是以键值对为形式存在的,key是String value是Object。其封装的数据如下: ...
  • q547550831
  • q547550831
  • 2016年11月24日 20:17
  • 2366

struts2 Map<String,Object>session心得 浅析

我们知道,struts2中对底层的web容器对象做了二次的封装,封装成Map类型的类型用于封装全局的变量,在实践当中对于session用的比较多;那么在struts2中我们有几种方法去获得这种对象呢?...
  • rxt2012kc
  • rxt2012kc
  • 2015年09月23日 19:31
  • 1538

List<Map<String, Object>>直接取出每个对象中固定的key值

package com.codyy.sso.controller.yuanqu; import java.util.ArrayList; import java.util.HashMap; impo...
  • qq541005640
  • qq541005640
  • 2017年07月11日 19:59
  • 632

用EL表达式取List<Map<String, Object>>map的值(查阅了很久,总结一下)

1.首先是查询到List数据 1.然后在jsp遍历取出,总结我在网上查到的“技能”,首先遍历List,得到map对象list,再通过EL表达式${map.['KEY']},在这个例子中即是${...
  • cooopa
  • cooopa
  • 2016年02月18日 22:18
  • 1601

List<? extends Map<String, ?>> 如何理解呢?

这是泛型。List 是一种集合,里面可以存放各种 Object 对象。尖括号框起来则限定了它可以存放的数据类型。例如 List 就只能存放 Integer 对象,你这里的 List> 则表示它只能存放...
  • RationalGo
  • RationalGo
  • 2013年08月06日 14:12
  • 1523

Freemarker遍历键值为对象的map

由于freemarker内部方法仅支持key为string类型的map(具体原因参考文章最后),为解决key为其他对象的问题,特写此例,本实例中map的键为Integer,其他对象的键值可采用类似的方...
  • u013419791
  • u013419791
  • 2015年11月05日 18:13
  • 1035

ajax 返回数据,alert后显示object类型,如何处理

今天在做ajax调用webapi接口的时候,用的是fidder调试的。能看到返回的数据是xml,类似于json格式,用alert(data),始终都是object类型的。但是在读取值的时候,始终不知道...
  • qq_31971935
  • qq_31971935
  • 2016年09月06日 09:15
  • 1977

EL表达式获取List<Map<String, Object>>中的map的值

后台controller  String nbfylr=util.getPropertyName("logiInfo.function.wlsq_wx01_nbfylr");  model.add...
  • colin_hui_
  • colin_hui_
  • 2017年07月12日 16:31
  • 256
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:_ATL_OBJMAP_ENTRY Com中的Object Map是怎么建立起来的
举报原因:
原因补充:

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