写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