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

写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 会排成一个数组.

 

推荐一遍文章 http://blogs.msdn.com/b/larryosterman/archive/2004/09/27/234840.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值