1、1,第15章 利用ATL编写COM组件,教学要点本章内容主要包括COM组件与软件复用、ATL与模板的概念,利用Access建立数据库,创建COM组件、使用COM组件。要求了解模板的概念、使用COM组件的方法,熟悉与COM组件有关的概念,掌握利用ATL创建COM组件的方法,熟练掌握在VC中调用自己创建的组件的方法和在别的开发工具如VB中使用VC创建的COM组件的方法。,2,利用ATL编写COM组件,本章目录15.1 COM组件与ATL15.2 利用ATL创建COM组件 15.3 在VC和VB中使用COM组件习题,3,15.1 COM组件与ATL,本节目录 15.1.1 COM概述 15.1.2
2、COM与软件复用 15.1.3 与COM有关的概念 15.1.4 ATL简介,4,15.1.1 COM概述,COM是组件对象模型,在Windows操作系统下COM组件很多,组件是C+面向对象思想在软件业的应用,它是一种创建二进制软件组件的体系,是一种与平台无关、语言无关、分布式的面向对象的体系。它不是一种面向对象的语言,而是一种标准,COM描述了如何创建对象的一个模型以及在COM对象间进行通讯所需要的内容。因此,从功能上讲COM更像一个协议,一个二进制级别上组件重用的协议。,5,15.1.2 COM与软件复用,在软件开发过程中,人们提出软件的制造能否像硬件的生产一样,实现规约化的组装。答案是肯
3、定的,利用Microsoft的COM技术编写的组件就可以达到这个目的。软件复用避免了软件开发过程中的重复劳动的解决方案,其出发点是应用系统的开发不再采用一切“从零开始”的模式,每个COM组件有自己的属性和操作,是一个相对独立运行的实体。从软件复用种类的定义看,COM技术实质上是一种模块复用的情况。采用组件开发软件就像搭积木一样容易,组件单独开发并作为软件模块使用,它具有明确的接口,客户通过这些接口调用组件所能提供的服务,将多种组件拼装起来就能构成大型的组件以至建立整个系统。,6,15.1.3 与COM有关的概念,1对象对象代表现实中的某个实体,是类的实例。在COM中,对象也是类的实例,但是CO
4、M对象与面向对象模型的类对象有区别。COM是与软件开发语言无关、平台无关,而面向对象模型中的类对象在不同的语言下是有区别的。 2接口动态链接库DLL是通过导出一系列函数来让客户程序使用的,只有知道了DLL的导出函数列表才能使用它。COM组件的载体也是一个DLL,COM组件是通过接口与客户进行通讯的,但它是一个抽象类(含有纯虚函数的类)。下面是一个接口的简单定义:,7,与COM有关的概念(2),Interface Ideposit:Iunknown virtual HRESULT GetMoney (int *nMoney)=0; ; Iunknown接口很重要,其定义如下: Struct Iu
5、nknown virtual HRESULT QueryInterface(REFID iid,void * * ppvobj)=0; /查询是否支持接口 virtual ULONG AddRef()=0; /引用计数加1 virtual ULONG Release()=0; /引用计数减1 ,8,与COM有关的概念(3),3类与类工厂 类是COM接口的实现,所有的COM对象都是COM类的实例。一个COM类也是用一个GUID来标识,在类中称它为“类标识符”(CLSID:Class Identifier)。每个类也有一个字符串名称,叫做“编程标识符”. 4组件组件就是实现了指定接口并被封装在DL
6、L或可执行文件中的软件模块,它能够实现由接口指定的功能,主要用来创建COM对象的。一个组件包括该COM类和实现该类工厂的程序代码,有时也称它为“服务器”。一个组件可以包括多个COM对象,每个COM对象可以实现多个接口。,9,与COM有关的概念(4),5注册与注册表组件编好之后,编译出来的DLL文件还必须在注册表中注册后才能被其它用户使用。因为只有这样,用户在创建COM对象时,系统才能在注册表中获取相应对象的位置,才能创建对象,对于DLL型的组件,需要调用RegSvr32.exe来完成,对于EXE型的组件,只要服务器运行过一次,该组件就会自动进行注册。返回,10,15.1.4 ATL简介,ATL
7、(ActiveX Template Library,活动模板库)是一套C+的模板库,利用它可以很方便地建立小型的、基于COM的组件,对COM组件的开发提供了最大限度的代码自动生成以及可视化的支持。模板是ATL的核心技术。 1什么是模板模板(Template)是基于类型参数产生类和函数的一种机制。模板在不降低类型安全的基础上,可以显著地减少代码量,增加代码的灵活性。模板包括函数模板和类模板,本节只讲类模板。 2类模板类是对问题空间的抽象,而类模板则是对类的抽象,即更高层次上的抽象。使用类模板,可以指定操作于某个数据类型的一族类。在程序中可以首先定义一个类模板,然后通过使用不同的实参生成不同模板类
8、的对象。,11,ATL简介(续),类模板的定义方法为:template class 类名;类模板的具体内容与普通类没有本质的区别,只是其员中要用到类模板的类型参数。【例15-1】一个类模板实例。程序的主文件名为MyTemplate.cpp,12,15.2 利用ATL创建COM组件,本节以一个字符串处理的组件为例来阐述如何利用ATL来制作COM组件。编程的思路为:(1)在VC下创建一个ATL工程,并对工程的可选项进行设置;(2)向工程中添加ATL对象,并对该对象进行一些初始配置工作;(3)根据COM组件的功能,向新的ATL对象中加入新的接口定义,包括方法和事件的定义,并实现相应的接口成员函数;(
9、4)编译链接该工程,注册COM组件;(5)编写组件测试程序,验证组件的功能。,13,利用ATL创建COM组件,本节目录 15.2.1 创建ATL应用程序框架 15.2.2 在工程中添加ATL对象 15.2.3 在COM组件中添加方法 15.2.4 ATL中的字符串数据类型 15.2.5 编译IDL并注册COM组件,14,15.2.1 创建ATL应用程序框架,1在VC开发环境下,选择File|New菜单,将弹出的New对话框切换到Projects选项卡后,选择ATL COM AppWizard项,在Project name编辑框中输入MyCom工程名称,并设置好工程所在的文件夹。如图右图所示。,
10、15,创建ATL应用程序框架(续),2单击“OK”按钮,进入图15.2所示的选择ATL组件类型对话框。,15.2 选择COM组件类型对话框,16,创建ATL应用程序框架(续),ATL组件类型一共有三种:第一种是DLL类型;第二种是EXE类型;第三种是Service类型,它也是一个典型的进程外组件。Service是Windows2000的一种应用服务,当操作系统启动的时候,该Server自动运行。由于进程外组件的执行效率较低,我们选择DLL类型的。选择完后,按“Finish”按钮,结束ATL创建组件的向导生成步骤,最终在磁盘上生成了组件的框架代码。表15.1列出了该工程中的主要文件的信息。,17
11、,表15.1 AppWizard生成的MyCom工程中的文件 return,18,15.2.2 在工程中添加ATL对象,1.选择Insert|New ATL Object,弹出如图15.3所示的对话框,从Objects列表框中选择Simple Object项后,单击“Next”按钮继续。,图15.3插入一个基于ATL的COM类,前面已经创建了一个MyCom工程,接下来,使用VC的ATL Object Wizard工具,可以方便地往工程中添加ATL对象。,19,15.2.2 在工程中添加ATL对象(续),2系统弹出一个ATL Object Wizard Properties对话框。在Short
12、Name中输入Str,在COClass编辑框中输入StrCom,如图15.4所示。注意在Name选项卡中,可以键入类与文件名。默认情况在,Short Name名字是其它名字的词根。可以不接收默认的名字而键入自己的名称。图15.4窗口中的Class是对象所实现的类的名称,CoClass为包含对象支持的一些接口的名称,Interface为对象创建的接口名字,Type为有关注册对象的描述性字符串,Prog ID为容器所使用的名字以代替对象的CLSID。,图15.4 设置COM类的属性,20,15.2.2 在工程中添加ATL对象(续),3切换到图15.5所示的Attribute选项卡,设置StrCom
13、对象的属性,如组件的线程模式、接口类以及是否支持聚集等属性。我们使用系统的默认属性。单击“OK”按钮,就生成了一个StrCom组件对象。,图15.5 设置组件对象的属性图,21,15.2.3 在COM组件中添加方法,1方法的添加 进入Workspace窗口的ClassView面板,在IStr接口上右击鼠标,选择Add Method菜单项后,弹出如图15.6所示的增加方法接口对话框,在Method Name编辑框中输入FirstToUpper,在Parameters编辑框中输入“in BSTR InData,outBSTR * OutData,out,retvallong *result”后,按
14、OK按钮结束方法的添加。,图 15.6 添加FirstToUpper方法,22,15.2.3 在COM组件中添加方法(续),第一个参数为InData,它是具有“in”属性的字符串变量,它是输入参数,代表欲转换的字符串;第二个参数为outData,它是具有“out”属性的字符串变量,它是输出参数,代表转换完后的的字符串;第三个参数为result,它是具有“out”和“retval”属性,意思是说该参数是作为返回值来用的输出参数,其类型是指向长整型的指针变量。此时用户在ClassView中就可以看到Istr接口中的FirstToUpper的方法。如图15.7所示。,图15.7 Istr接口的方法F
15、irstToUpper,23,15.2.3 在COM组件中添加方法(续),2方法的实现在Workspace窗口的ClassView面板中,将类CStr展开,接着,再将该类中的Istr接口展开,双击方法FirstToUpper(char *source, char *destion),光标定位到该类的实现文件处,在其中添加代码。STDMETHODIMP CStr:FirstToUpper(BSTR InData, BSTR *OutData, long *result) CComBSTR strSource=InData;bool bMayUpper=true; /是否可以大写的标志for(in
16、t i=0;i=a,24,15.2.4 ATL中的字符串数据类型,由于COM组件是跨平台与开发语言的,利用ATL开发出来的组件在VB等别的开发工具下也可以使用。ATL中使用的是C+语言,别的开发工具所使用的语言和C+的数据类型是有区别的,那么在不同的语言之间如何进行数据的交换呢?这要靠组件接口中提供的多语言通用类型的数据类型,规定通用类型也是COM组件的一个组成部分。,25,15.2.4 ATL中的字符串数据类型(续),1BSTR 用COM技术来设计组件以及构件系统时,字符串常用BSTR来表示。BSTR是BASIC String的简称,常用于BASIC的编程中。该类型是包含长度前缀的,以0结束
17、的字符串,长度前缀表示该字符串所包含的字节数。COM提供了一些API函数来管理BSTR。如SysAllocString()(创建并初始化一个字符串),SysAllocStringLen()(创建一个指定长度的字符串),SysFreeString()(释放一个字符串),SysReAllocString()(改变字符串的长度),SysStringLen()(返回字符串的长度)等。在VC客户端一般调用SysAllocString()创建字符串,以In参数方式传递给组件中的方法,调用完成后用SysFreeString()来释放字符串的缓冲区。 BSTR str= SysAllocString(”are
18、 you ready?”);HRESULT hr=p-FirstToUpper(str);SysFreeString(str);,26,15.2.4 ATL中的字符串数据类型(续),2CComBSTRCComBSTR是ATL封装的一个来使用BSTR数据类型的类,如果把BSTR看作各种语言都可以使用的共同数据类型的话,那么CComBSTR则就是在C+下操纵COM中的一个数据类型。CComBSTR类中有一些重要的函数,如Append()(增加一个字符串),AppendBSTR()(增加一个BSTR字符串),Copy()(返回成员变量m_str的值),Empty()(释放成员变量m_str的值),L
19、ength()(返回BSTR的长度)等。,27,15.2.5 编译IDL并注册COM组件,1IDL简介COM接口通常用“接口定义语言”IDL来定义,IDL是一种类似于C+的语言,前一节设计了一个MyCom组件,下面是其接口MyCom.idl的内容:2编译IDL上述Mycom.idl文件我们并未编写,这完全归功于ATL工具,是它帮助我们自动完成了IDL接口文件。前面在给组件中添加方法的过程中,向导自动完成了IDL文件。切换到WorkSpace的FileView面板中,在MyCom.idl文件上右键单击鼠标,选择Compile MyCom.idl菜单项,于是在磁盘上生成了接口定义文件对应的二进制文
20、件Mycom.tlb。该文件中含有接口中所定义的方法的记录,定义了组件的虚函数指针表。.tlb可以看作是二进制的接口定义语言。,28,15.2.5 编译IDL并注册COM组件(续),/ MyCom.idl : IDL source for MyCom.dll / This file will be processed by the MIDL tool to / produce the type library (MyCom.tlb) and marshalling code. import “oaidl.idl“; import “ocidl.idl“; object,uuid(F1CDC04
21、3-28E4-44D0-A4A6-BB8654CD7C02),dual,helpstring(“IStr Interface“),pointer_default(unique)interface IStr : IDispatchid(1), helpstring(“method FirstToUpper“) HRESULT FirstToUpper(in BSTR InData,outBSTR * OutData,out,retvallong *result);uuid(D30786A1-E559-43D4-A62B-9A9FA0C559B1),version(1.0),helpstring(
22、“MyCom 1.0 Type Library“),29,15.2.5 编译IDL并注册COM组件(续),library MYCOMLib importlib(“stdole32.tlb“);importlib(“stdole2.tlb“);uuid(9618DF64-B84E-43BF-8A9C-3634E6A9F88F),helpstring(“Str Class“)coclass StrComdefault interface IStr; ;,30,15.2.5 编译IDL并注册COM组件(续),3注册MyCom.dll要想让用户能够“透明”地使用创建的组件,首先必须将该组件注册。单击B
23、uild|Build Mycom.dll菜单项,VC编译器将生成该组件文件MyCom.dll,并且自动完成注册。其中注册的代码在MyCom.cpp中。还有一种手工注册组件的方法是利用RegSvr32.exe来进行,请参阅15.1.3中的5小节注册与注册表的说明。,31,15.3 在VC和VB中使用COM组件,本节内容 15.3.1 调用COM组件的过程 15.3.2 在VC中创建COM组件客户 15.3.3 在VB中使用COM组件,32,15.3.1 调用COM组件的过程,Windows系统下有大量的组件可供使用,前面我们所创建的MyCom组件也注册好了,因此也可以使用,使用COM组件过程是一
24、个典型的客户/服务器(Client/Server)方式。COM组件含有被调用的接口,它是提供服务的,因此是服务方。Windows系统下的组件如同一大堆积木,不同的人建房子所使用的积木也不一样,因此不同的人使用的COM组件也不一样,但它们有共同的使用过程:首先客户程序要获取包含组件的服务器(如MyCom.dll就是提供了字符串转化的服务器),然后让服务器创建组组件相关联的类工厂,再由类工厂实例化组件,最后通过接口来使用组件。,33,15.3.2 在VC中创建COM组件客户,1打开上一节的MyCom工程后,选择File|New菜单,将弹出的New对话框切换到Projects选项卡后,选择MFC A
25、ppWizard(exe)项,在Project name编辑框中输入ComClient工程名称,并选择Add To Current Workspace单选择按钮。如图15.8所示。,图15.8 在MyCom工作区中添加MyComClient工程,34,15.3.2 在VC中创建COM组件客户(2),2.单击“OK”按钮进入向导下一步骤,选择单文档应用程序类型。完后按“Finish”按钮生成ComClient工程(这是在一个工作区中包含两个工程的情况)。 3.在对话框资源IDD_COMCLIENT_DIALOG中添加图15.9所示的控件,这些控件的属性如表15.2所示。,图15.9对话框界面设计
26、,35,15.3.2 在VC中创建COM组件客户(3),表15.2控件的属性设置与对应的成员变量设置 return,36,15.3.2 在VC中创建COM组件客户(4),4.在工程中添加Istr类按CTR+W打开类向导对话框后,在当前的工程中加入COM中的一个类。方法为在类向导的Message Map选项卡下,单击“Add Class”按钮,弹出选项“From a Type Library”如图15.10。单击“From a Type Library”选项,出现图15.11的对话框,把Mycom.tlb选上,又出现图15.12的对话框,把Istr类选上,按“OK”按钮,这样就把创建的MyCom
27、.tlb中的类Istr加入到该工程中。,图15.10从类型库中增加类,37,15.3.2在VC中创建COM组件客户(5),返回,图15.11 选择Mycom.tlb 图15.12选择类型库中的IStr类,38,15.3.2 在VC中创建COM组件客户(6),5主要代码(1)在应用程序类的InitInstance()函数中增加初始化OLE的代码BOOL CComClientApp:InitInstance() AfxEnableControlContainer();AfxOleInit(); /初始化OLE (2)“转换”按钮的实现代码void CComClientDlg:OnButton1()
28、 IStr m_string;if(!m_string.CreateDispatch(“MyCom.str.1“) /初始化COM库MessageBox(“Create UserInfo error“);UpdateData(true); /将前台数据传到后台BSTR bstrDestion=SysAllocString(L“); m_string.FirstToUpper(m_strSource, /将后台数据传到前台,39,15.3.3 在VB中使用COM组件,前面用ATL创建了一个COM组件MyCom,该组件包含有一个类型库。因为类型库使得可以在COM中实现语言集成,可以很方便地在其它语
29、言编程环境中使用该组件。,40,15.3.3 在VB中使用COM组件(2),【例15-3】在VB中调用VC所编的COM组件。下面讲解在VB6.0的环境创建使用组件的客户程序。1启动Visual Basic6.0应用程序,启动时(或选择File|New菜单项)出 现NewProject 对话框,选择New选项卡中的Standard EXE项,如图15.13所示。2在如图15.14所示的界面中放几个标签控件、按钮控件并设置其属性。3将自己制作的MyCom组件插入到该VB应用程序。选择Project|Reference菜 单项,弹出如图15.15所示的界面,选择MyCom 1.0 Library项。
30、按“OK”按钮关闭 Reference对话框。,41,15.3.3 在VB中使用COM组件(3),图15.13 选择应用程序类型对话框,图15.14 设置界面及控件的属性,图15.15 Reference对话框,返回,42,15.3.3 在VB中使用COM组件(4),4编写“转换”按钮的代码Private Sub Command1_Click()Dim MyStrCom As StrComSet MyStrCom = New StrCom 建立COM对象Dim strSource As StringDim strDestion As StringDim tempStr As StringDim
31、 result As IntegerstrSource = Text1.Text 取源串result = MyStrCom.FirstToUpper(strSource, strDestion) tempStr = strDestionText2.Text = tempStr 将转换后的字符串在文本框中进行显示 End Sub,43,15.3.3 在VB中使用COM组件(5),5运行程序,并在“源串”编辑框中输入一行字符,单击“转换”按钮,则将该行字符串的首字母变为大写,并显示在“目标串”编辑框中。,44,习题,一、名词解释1 类模板 2ATL 3COM 4 IDL 5接口 二、简单题1查看MSDN和相关资料,分析普通的.DLL文件和COM 组件的存放形式之一的.dll有哪些区别?2以您对数据类型的理解,谈谈COM中的字符串类型BSTR和MFC中的CString、C语言中的字符数组的概念之间的异同? 三、实验题1在本章实例的基础上,在COM组件中再增加一个求字符串中所含单词个数的方法。2VC和VB下,分别编写程序来调用该组件中的方法。,