一、在ATL3.0中,是通过对象映射表来注册服务器。
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_MyCircleCollectionCreator, CMyCircleCollectionCreator)
END_OBJECT_MAP( )
当ATL注册服务器时,最终调用到AtlModuleRegisterServer函数进行服务器注册。对于组件支持的所有对象,它创建一个注册表项进行注册,注册过程结束。
但是,在ATL7中,以上三个宏都已经过时,取而代之的是宏OBJECT_ENTRY_AUTO。下面我们就来分析宏OBJECT_ENTRY_AUTO的实现。
二、宏OBJECT_ENTRY_AUTO的定义。
1. 我们首先看看_ATL_OBJMAP_ENTRY的定义:
struct _ATL_OBJMAP_ENTRY30
{
const CLSID* pclsid;
HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
_ATL_CREATORFUNC* pfnGetClassObject;
_ATL_CREATORFUNC* pfnCreateInstance;
IUnknown* pCF;
DWORD dwRegister;
_ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
_ATL_CATMAPFUNC* pfnGetCategoryMap;
};
typedef _ATL_OBJMAP_ENTRY30 _ATL_OBJMAP_ENTRY;
2. 宏OBJECT_ENTRY_AUTO的定义如下:
#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)
3. 接下来我们看一个辅助的宏OBJECT_ENTRY_PRAGMA,它的定义如下:
#define OBJECT_ENTRY_PRAGMA(class) \
__pragma(comment(linker, "/include:___pobjMap_" #class));
三、宏OBJECT_ENTRY_AUTO的功能
1. 结构体_ATL_OBJMAP_ENTRY就不用多说了,和ATL3是完全一样的。
2. 宏OBJECT_ENTRY_AUTO的功能:
1) 使用clsid, class的信息,生成_ATL_OBJMAP_ENTRY的一个实例,变量名为__objMap_加上class的实际值。
2) 声明1)所生成实例的一个指针,并赋值为1)生成的实例的地址。但是指针被放在ATL$_m段中。
3. 宏OBJECT_ENTRY_PRAGMA的功能是告诉连接器向符号表中添加符号___objMap_加上class的实际值。注:在X86平台下,符号名为声明的名字前加下划线。所以在OBJECT_ENTRY_PRAGMA宏中,pobjMap之前有三个下划线。
四、在哪部分代码中使用OBJECT_ENTRY_AUTO
创建ATL7.0工程时:
1. 如果使用的是非属性化编程,那么它在COM类的头文件中,它由向导自动生成,千万不要删除该宏,否则将不能够创建该类的实例。 而这样的错误又是非常难发现的。
2. 如果使用的是属性化编程,那么在COM类的前面有coclass属性,编译器将向代码中插入一些代码完成以下功能:
l 添加COM类的基类
l 产生注册代码
l 产生接口映射表
l 产生对象映射项,即宏OBJECT_ENTRY_AUTO
所以在代码中,你实际看不到OBJECT_ENTRY_AUTO。
五、OBJECT_ENTRY_AUTO和系统其他部分的协作,这里分析对象映射表的建立及使用。
如果使用非属性化编程,那么用户可以生成任意合法COM类名称;如果使用属性化编程,我们根本不知道_ATL_OBJMAP_ENTRY指针变量的名字,这些对于ATL库来讲,它不可能预先知道用户定义的COM类的名字。那么它是如何遍历对象映射表,来进行更新注册表呢?
答案在于对象映射表的建立,我们先来看映射表的开始项、结束项。他们的定义如下:
__declspec(selectany) __declspec(allocate("ATL$__a")) _ATL_OBJMAP_ENTRY* __pobjMapEntryFirst = NULL;
__declspec(selectany) __declspec(allocate("ATL$__z")) _ATL_OBJMAP_ENTRY* __pobjMapEntryLast = NULL;
分析如下:
l 需要注意的是,__pobjMapEntryFirst、__pobjMapEntryLast分别被放置在段ATL$_a和ATL$_z中。
l 再次需要注意的是,通过宏OBJECT_ENTRY_AUTO放置的变量在ATL$_m中。
l 当连接器布局代码时,它按根据的名称,按照字母排序的规则,排列所有段。这样在段ATL$_a中的变量出现在段ATL$_z所有变量之前。
l 最后使用#pragma comment(linker, "/merge:ATL=.rdata"),将三个段中的所有数据合并到段.rdata只读数据段中。
l 最后形成的布局可能如下:
__pobjMapEntryFirst
…
UserDefinedPtr1
UserDefinedPtr2
…
__pobjMapEntryLast
通过这种方式,就形成了对象映射表。而在ATL库中,可以根据__pobjMapEntryFirst、__pobjMapEntryLast来定位对象映射表,对其中的所有对象进行注册。
本文地址:http://com.8s8s.com/it/it357.htm