2023年11月30日发(作者:)

ATL基本使用

这一部分将重点介绍ATL的基本使用过程。由于ATL已经被集成在Microsoft Visulal StudioVisual

C++开发环境中,因此要使用ATL必须先安装Visual C++。在下面的讨论中有关COM的基本知识请参阅

有关的文档,这里不再详细说明。给出的图是在Microsoft Windows 98平台下Visual Studio 6.0的使用示

意图。

使用ATL开发一个COM应用基本可以分为以下几个步骤:

创建一个新的ATL工程,并对工程的选项进行适当的配置。

向新创建的工程添加新的ATL类,并对该类进行一些初始配置工作。

根据COM应用的基本要求向新的ATL类加入新的接口定义,并实现相应的接口成员函数。

编译连接工程,注册COM应用。

下面将根据这些步骤依次介绍ATL的基本使用过程。

1. 创建工程

首先启动Visual C++集成开发环境,选择“File”菜单下的“”命令,在“New”对话框中选择“Project”

页,如图1所示。

1 创建新工程界面示意图

选择“ATL COM AppWizard”项,这是创建ATL工程的AppWizard向导入口。然后在“Project name”

编辑框中输入工程的名字,单击“OK”按钮,进入AppWizard对话框。如图2所示。

2 ATL COM AppWizard对话框示意图

AppWizard对话框中主要的设置选项有:

COM服务程序的类型:

- 动态连接库(Dynamic Linking Library 最终产生一个动态连接库(DLL)形式的COM服务程序;

- 应用程序(Executable application)最终产生一个可执行程序类型(EXE)COM服务程序;

- NT服务(NT Service:产生一个以NT服务方式运行的COM服务程序。

允许嵌入Proxy/Stub代码。由Microsoft提供的MIDL编译IDL文件以后,将产生用于对象调度

Marshaling)的Proxy/Stub的代码。传统地,这部分代码与COM服务程序的代码是分离的,但是由于

新的COM标准支持多线程环境下的COM对象服务,因此在动态连接库的COM服务程序中也要有

Proxy/Stub的支持。为了支持在网络上的传输,ATL允许用户选择将Proxy/Stub的代码包括在生成的DLL

代码中。这个选项在EXENT服务类型的COM应用条件下不可选。

允许支持MFC。由于ATL对除COM以外的基本的Windows编程方面的支持极为有限,同时许多程

序员对MFC又非常熟悉,因此在ATL的工程设置中允许在ATL工程内部支持使用MFC即可以使用MFC

定义的类。这在一方面来看是非常方便的,特别是对于习惯于使用MFC的开发人员来说,能够使用MFC

提供的各种功能强大的类的支持,而不必直接使用Windows SDK。从另一个方面来看,在ATL工程中使

MFC同时就丧失了ATL代码轻量级的特点。

支持MTSMTSMicrosoft Transaction Server的缩写,它是MicrosoftCOM技术方面的一个新

的分支,这里不作详细说明。

完成上面的设置以后,可以选择FINISH完成工程的设置,ATL将创建相应的工程。

2. 加入ATL

完成工程的创建和设置以后,下一步就是向工程中加入一个新的ATL类。Visual Studio集成环境提供

了向导工具“ATL Object Wizard”用于加入一个新的ATL类。操作过程并不复杂,只是一组对话框操作而已。

首先通过集成环境的“Insert”菜单下的“New ATL Object…”命令进入“ATL Object Wizard”对话框,如图3

所示。

3 ATL Object Wizard对话框示意图

这个对话框即为创建ATL对象的向导起始界面。对话框的左边部分说明了待创建对象的基本类型,这

里主要有以下的几种类型:

对象(Object)基本的COM对象类型;

控制(ControlActiveX Control类型的ATL对象;

其他(Miscellaneous)辅助功能,如对话框的生成等;

数据访问(Data Access)数据访问,支持MTS等。

右边部分说明了每种类型的详细内容,对于一般的COM服务程序使用对象表中的简单对象Simple

Object)就可以了。

选定待创建对象的基本类型以后,单击“Next>”按钮进入下一步,进入对象属性设置对话框,如图4

5所示。

对象属性设置分为两个过程:先是对象名字标识的设定,然后是对对象的基本属性进行设置。首先是

对象的名字标识设置,如图4所示。

4 对象名字标识设置对话框示意图

在对象标识编辑框中输入待创建对象的名字,ATL对象向导将同步地根据用户输入的对象标识设定该

对象的C++标识和COM标识。对象的C++标识包括对象的类名,cpp文件名和头文件名。COM标识包括

对象在类型库中的CoClass段和实现的主接口的名字,同时还有在系统注册表中的类型名以及ProgID

对象名字标识设置完成以后,选择对象属性页(Attribute)进入对象的属性设置页面,如图5所示。

5 对象属性设置对话框示意图

对象的属性设置是ATL对象创建过程中最复杂的部分,包括以下几个主要部分:

对象的线程模型Thread Model

对象的线程模型是COM对象在多线程环境下被访问时对访问方式的控制,缺省情况下在ATL中采用

的是套间模型Apartment,由系统通过消息队列方式提供并发控制。

对象的接口模型(Interface

COM对象的接口可以是双接口(Dual Interface)。双接口不同于普通接口(Custom Interface) 之处在于

双接口是从Automation基本接口IDispatch继承的,而普通接口是从IUnknown接口直接继承来的。缺省

的接口模型是双接口。

对象的聚合模型(Aggregate

COM规范不允许对象的实现继承,但是可以通过聚合方式重用其它的COM对象。ATL对象属性设置

中的聚合模型可以指定待创建的COM对象是否支持聚合模型。缺省的选项是支持对象的聚合。

对象对错误处理的支持(Support ISupportErrorInfo)

选取这个选项可以在对象的运行过程中支持错误处理。缺省情况下这个选项不被选中。

对象对连接点的支持(Support Connection Points)

连接点是COM对象的事件机制。选中这个选项可以使待创建的COM对象具有发出事件的能力。缺

省情况下该选项不被选中。

对象对自由线程调度的支持(Free Thread Marshaller, 简称FTM)

对象的自由线程调度是对象在处于自由线程模型状态下,为了简化对象的访问过程而采用的一种优化

策略。缺省情况下该选项不被选中。

对于上述的任何一个选项的详细描述都涉及到COM技术一些核心的内容,并且都已超出本文的范围,

因此本文只对ATL给出的缺省选项加以说明,对这些内容感兴趣的读者可以参考Microsoft提供的文档。

完成了上面的设置以后,就可以按“OK”按钮完成对象的创建过程。下一步就是向所生成的ATL类的接

口中加入成员函数的定义,以及接口成员函数的实现过程。

3. 加入接口定义,实现接口函数

加入了ATL类定义之后,我们可以打开Visual C++集成环境下项目管理器(Workspace)中的Class

View来检查生成的类定义的情况(见图6)。我们可以看到一个新的类已经生成,同时,还生成了相应的接

口定义。ATL Object Wizard为我们生成了类定义的.h .cpp文件,此外还有用于接口定义的IDL文件。

有了这些文件以后,我们就可以为接口加入成员函数,完成类的定义。

6 ATL工程的ClassView示意图

首先在Class View中选中相应的接口,6中显示为接口IATLTest单击鼠标右键打开菜单,如图7

此弹出式菜单定义了为接口加入属性和方法的操作。选取其中的“”项,可以为接口加入方法

成员;选取“”则可以为接口加入新的属性成员。

7 接口编辑菜单示意图

加入属性和方法的对话框可以参看图8和图9。如果我们要在接口中加入一个方法,则选取“Add

菜单命令。假设方法名为ABC,方法的返回类型为COM规定的HRESULT类型。我们也可以

定义非HRESULT返回类型的函数,但是这需要手工修改接口定义的IDL文件。我们定义ABC方法的一

个参数为a,类型为整数型。完成了方法的定义以后,单击“OK”按钮则把此方法加入到接口中。

属性的加入过程是类似的。属性加入对话框要求指定属性的类型、名字以及属性的访问方式。在属性

和方法的编辑对话框中都有一个“Attributes”按钮,在给出了一个属性或方法的基本定义之后,单击此按钮,

可以对属性和方法的一些高级特性进行设置。

8 加入接口方法的界面

9 加入接口属性的界面

方法成员加入以后,我们可以通过Class View来检查ATL为我们所做的工作,如图10所示。首先我

们看到ATL在接口的定义中加入了该方法的定义;同时在对应的ATL类定义中,也加入了一个相应的方

法的定义;在类对应的.cpp文件中,加入了此方法的实现框架。此后,我们只要在这个函数框架中加入该

方法的代码逻辑,一个接口函数的定义和实现就基本完成了。依照这种方式,我们可以完成整个COM

象的定义和实现。

10 加入接口方法以后的Class View

完成以上的步骤之后,我们就可以编译连接应用了。

4. 编译连接应用、注册COM服务程序

ATL工程的编译连接过程包括下面的几个步骤:

使用MIDL编译工程的IDL文件,形成接口定义的头文件和用于调度(Marshalling)的代码;

编译工程的.cpp文件形成目标文件;

连接目标文件,形成应用模块;

注册COM服务程序。

关于工程编译连接的其它部分同Visual C++MFC工程的编译连接过程相似,这里只重点介绍一下

COM服务程序的注册过程。

ATL中,COM服务程序的注册是在工程编译连接的最后阶段,ATL辅助完成的。在手工的COM

编程中,服务程序的注册是比较麻烦的工作。在ATL中,系统通过读取在建立工程过程中形成的注册脚本

文件来完成注册工作。注册脚本(Register Script 简称RGS)ATL提供的文本方式的注册辅助文件。下面

是注册脚本文件的一个实例。

HKCR 表示注册表中COM对象的注册项,是HKEY_CLASS_ROOT的缩写

{

XObject.1 = s 'ActiveXObject Class'

{

CLSID = s '{97A5CB10-AF82-11D2-B9BC-00104B04B867}'

} 对象的ProgID

XObject = s 'ActiveXObject Class'

{

CLSID = s '{97A5CB10-AF82-11D2-B9BC-00104B04B867}'

} -对象的与版本无关的ProgID

NoRemove CLSID -对象CLSID注册项

{

ForceRemove {97A5CB10-AF82-11D2-B9BC-00104B04B867} = s 'ActiveXObject Class'

{

ProgID = s 'XObject.1'

}

RGS文件包含注册COM服务程序的各项内容,通常我们不必修改此RGS文件,必要时我们也可以

手工修改RGS文件来定制模块的注册过程。

. 应用ATL的一个例子

上面介绍了使用ATL创建一个COM服务程序的基本过程。在介绍过程中,我们实际上已经生成了一

COM服务程序的基本框架,只是没有填写实际的内容。在下面部分,我们实际开发一个十分简单的COM

服务程序,并且为它编写一段客户代码进行测试,使大家对使用ATL开发COM服务程序的过程有一个全

面整体的了解。

我们要开发的服务程序的功能很简单,它只实现一个接口,这个接口名字是ISimpleInterface,接口只

有一个成员函数,叫做Welcome。这个函数的功能只是输出一个“Hello World!”的字符串。

按照上一部分介绍的创建COM服务程序的步骤,我们进行如下的操作:

1 打开Visual C++集成开发环境;

2 创建一个称为SimpleTestATL工程;

3 在这个工程中插入新的对象,对象的名字是SimpleInterface

4 设置接口ISimpleInterface的有关属性,使它成为一个双接口;

5 在对象的接口ISimpleInterface中加入方法Welcome

6 打开ATL加入的Welcome方法的框架,可以看到如下的代码段:

STDMETHODIMP CActiveXObject::get_TestProp(long *pVal)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

// TODO: Add your implementation code here

return S_OK;

}

7 将程序框架中的注释部分替换为下面的代码:

::MessageBox(NULL,_T(”Hello World!”),_T(”Welcome”), MB_OK);

Welcome方法被调用时将弹出一个消息框。

8 编译连接工程。

上面的步骤完成以后,我们就有了一个简单的COM服务程序,而且已经被注册到当前系统中。

下面我们要完成一个简单的COM客户程序。一个COM客户程序简单地说是使用COM组件对象的程

序。客户程序调用COM对象的基本流程是:

创建COM对象的实例。这可以通过调用Windows系统的API函数CoCreateInstance来完成。

通过接口调用函数。

调用IUnknown::Release释放COM对象实例

我们的客户程序是使用MFC编写的一个基于对话框的简单应用程序。具体的过程如下:

1 打开Visual C++集成开发环境;

创建一个称为SimpleClient的基于对话框的MFC工程;

在对话框中加入一个按钮,名字为TEST

文件中加入如下的代码:

(1) cpp文件 #include “simpleclientdlg.h”之后加入下面的代码:

#include “d:simpletestsimpletest_i.h” // 根据需要修改头文件的路径

加入的头文件是在编译COM服务程序过程中自动生成的,其中包含接口本身的定义、接口IID的定义

COM对象的CLSID的定义。包含该头文件可以使客户程序能够使用COM服务程序。

(2) 在按钮TEST的消息控制函数中加入如下的代码:

HRESULT hr;

ISimpleInterface* pIntf = NULL;

hr = CoCreateInstance(CLSID_SimpleInterface, NULL, CLSCTX_SERVER ,

IID_ISimpleInterface, (void **)& pIntf);

if(SUCCEEDED(hr))

{

pIntf->Welcome();

pIntf->Release();

}

上面的代码首先通过系统API CoCreateInstance创建COM对象,得到接口的指针,然后调用接口成

员函数Welcome,最后通过IUnknown::Release()函数释放COM对象实例。

编译连接客户程序

最后,我们可以测试客户程序是否正常运行。启动客户程序,当单击“TEST”按钮时我们可以看到弹出

一个消息框,这正是我们的COM服务程序提供的功能。