ATL中关于进程内服务器注册机制的改变

类别:VC语言 点击:0 评论:0 推荐:

一、在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