使用ATL开发COM组件Word下载.docx
《使用ATL开发COM组件Word下载.docx》由会员分享,可在线阅读,更多相关《使用ATL开发COM组件Word下载.docx(15页珍藏版)》请在冰豆网上搜索。
第二,组件必须隐藏内部实现细节(独立于具体编程语言,二进制形式发布)。
每个组件相当于一个黑盒子,对外提供的只是接口(函数)。
如果接口没有发生任何变化时,对组件的修改几乎不会影响应用程序的其它部分。
提供服务的组件称为服务器组件,使用服务的组件称为客户组件。
6、COM的设计思想
COM的设计思想是为了重用,而且是二进制级别的代码重用。
类库也是重用,如MFC,但只是基于源码级别的重用,类库严重依赖于固定的某种语言,如MFC依赖于VC,PFC依赖于PowerBuilder等。
DLL也是重用,但也有很大的局限:
函数重名问题、各编译器对函数的名称修饰不兼容问题、路径问题、DLL与EXE的依赖问题。
COM没有重名问题,因为根本不是通过函数名来调用函数,而是通过虚函数表,自然也不会有函数名修饰的问题。
路径问题也不复存在,因为是通过查注册表来找组件的,放在什么地方都可以,即使在别的机器上也可以。
也不用考虑和EXE的依赖关系了,它们二者之间是松散的结合在一起,可以轻松的换上组件的一个新版本,而应用程序混然不觉。
7、学习COM的目的
要学COM的基本原理,推荐看《COM技术内幕》。
但仅看这样的书是远远不够的。
其实,我们最终的目的是要学会怎么应用COM去编写我们的应用程序,而不是拼命的研究COM本身的机制。
所以我个人觉得对COM的基本原理不需要花大量的时间去追根问底,没有必要,是吃力不讨好的事。
其实我们只需要掌握几个关键概念就够了。
8、用VC开发COM组件所要了解的几个关键概念
(1)、COM组件实际上是一个C++类,而接口都是纯虚类。
(2)、COM组件有三个最基本的接口类,分别是IUnknown、IClassFactory、IDispatch,其中,IUnknown包含三个函数,分别是QueryInterface、AddRef、Release。
(3)、Dispinterface接口、Dual接口以及Custom接口
(4)、COM组件有三种:
进程内COM、本地COM、远程COM。
即它可以是进程内的,即和调用者在同一个进程内;
也可以和调用者在同一个机器上但在不同的进程内;
还可以根本就和调用者在两台机器上。
(5)、COM组件的核心是IDL
IDL是一种用来定义COM接口规范的、大家都认识的接口定义语言,用它来定义的接口,不论放到哪个语言平台上都认识它。
用VC,VB等语言都可以开发COM组件,开发好的COM组件也可以被其他语言调用。
(6)、注册表在COM中的重要作用
首先要知道GUID的概念,COM中所有的类、接口、类型库都用GUID来唯一标识,GUID是一个128位的字串,根据特制算法生成的GUID可以保证是全世界唯一的。
COM组件的创建接口、查询接口都是通过注册表进行的。
有了注册表,应用程序就不需要知道组件的DLL文件名、位置,只需要根据CLSID查就可以了。
当版本升级的时侯,只要改一下注册表信息,就可以神不知鬼不觉的转到新版本的DLL。
(7)、一个典型的自注册的COMDLL所必有的四个函数
●DllGetClassObject:
用于获得类厂指针
●DllRegisterServer:
注册一些必要的信息到注册表中
●DllUnregisterServer:
卸载注册信息
●DllCanUnloadNow:
系统空闲时会调用这个函数,以确定是否可以卸载DLL
(8)、COM组件的运行机制
●通过查注册表CLSID_Object,得知组件DLL的位置、文件名
●装入DLL库
●使用函数GetProcAddress(...)得到DLL库中函数DllGetClassObject的函数指针
●调用DllGetClassObject
9、完整的COM组件的接口实现示例:
代码见“1.1.ComInterface1”工程,这里无需掌握,只是帮助理解COM的原理。
10、增加查询组件接口QueryInterface()、应用计数增加接口AddRef()、应用计数减少接口Release()后的完整例子。
代码见“1.2.ComInterface2”工程,这里无需掌握,只是帮助理解COM的原理。
二、ATL活动模板库(ActiveTemplateLibrary)
在ATL产生以前,开发COM组件的方法主要有两种:
一是使用COMSDK直接开发COM组件,另一种方式是通过MFC提供的COM支持来实现。
但是这两种方法都非常的复杂、麻烦。
使用ATL开发COM应用是一件比较简单的事情,但是在ATL简单易用的界面后面却包含着复杂的技术。
这些技术包含以下一些方面:
●COM技术
●C++模板类技术(Template)
●C++多继承技术(Multi-Inheritance)
其中,COM技术本文开头有所描述。
C++模板类技术(Template)、C++多继承技术(Multi-Inheritance)在C++理论课中已有详尽描述。
这里最主要的是介绍使用ATL怎么生成一个自己的COM组件,以及怎么使用该组件。
预备知识:
ATL中的数据类型BSTR
BSTR是BASIC中字符串类型的表示方式,是一个指向UNICODE字符串的指针。
有关BSTR的处理函数,参见下表。
API函数
说明
SysAllocString()
申请一个BSTR指针,并初始化为一个字符串
SysFreeString()
释放BSTR内存
SysAllocStringLen()
申请一个指定字符长度的BSTR指针,并初始化为一个字符串
SysAllocStringByteLen()
申请一个指定字节长度的BSTR指针,并初始化为一个字符串
SysReAllocStringLen()
重新申请BSTR指针
CString函数
AllocSysString()
从CString得到BSTR
SetSysString()
重新申请BSTR指针,并复制到CString中
CComBSTR函数
ATL的BSTR包装类。
在atlbase.h中定义
Append()、AppendBSTR()、AppendBytes()、ArrayToBSTR()、BSTRToArray()、AssignBSTR()、Attach()、Detach()、Copy()、CopyTo()、Empty()、Length()、ByteLength()、ReadFromStream()、WriteToStream()、LoadString()、ToLower()、ToUpper()
运算符重载:
!
!
=,==,<
>
&
+=,+,=,BSTR
太多了,但从函数名称不能看出其基本功能。
详细资料,查看MSDN吧。
另外,左侧函数,有很多是ATL7.0提供的,VC6.0下所带的ATL3.0不支持。
由于我们将来主要用ATL开发组件程序,因此使用ATL的CComBSTR为主。
ATL提供了CComBSTR类来简化使用BSTR(见上表)。
ATL中的数据类型VARIANT
C++、BASIC、Java、Pascal、Script......计算机语言多种多样,而它们各自又都有自己的数据类型,COM产生目的,其中之一就是要跨语言。
而VARIANT数据类型就具有跨语言的特性,同时它可以表示(存储)任意类型的数据。
ATL提供CComVariant类来简化使用VARIANT,如:
VARIANTv=CComVariant(任意C++类型);
下面我们使用ATL编写一个“将一个字符串转换成大写然后倒置”的COM组件:
1.建立一个ATLCOMAppWizard工程AtlComSample:
2.选择创建的COM类型:
该步骤中的一些选项说明:
动态连接库(DLL):
最终产生一个动态连接库形式的COM服务程序(DLL);
应用程序(Executable):
最终产生一个自注册的可执行的COM服务程序(EXE);
NT服务(Service):
产生一个以NT服务方式运行的COM服务程序(EXE)。
允许嵌入Proxy/Stub代码。
由Microsoft提供的MIDL编译IDL文件以后,将产生用于对象调度(Marshaling)的Proxy/Stub的代码。
在传统方式下,这部分代码与COM服务程序的代码是分离的,但是由于新的COM标准支持多线程环境下的COM对象服务,因此在动态连接库的COM服务程序中也要有Proxy/Stub的支持。
为了支持在网络上的传输,ATL允许用户选择将Proxy/Stub的代码包括在生成的DLL代码中。
这个选项在EXE和NT服务类型的COM应用条件下不可选。
允许支持MFC。
由于ATL对除COM以外的基本的Windows编程方面的支持极为有限,同时许多程序员对MFC又非常熟悉,因此在ATL的工程设置中允许在ATL工程内部支持使用MFC,即可以使用MFC定义的类。
这一特点给开发人员提供了许多方便,特别对于习惯使用MFC的人来说,能够使用MFC提供的各种功能强大的类的支持,而不必直接使用WindowsSDK。
从另一个方面来看,在ATL工程中使用MFC同时就丧失了ATL代码轻量级的特点。
支持MTS。
MTS是MicrosoftTransactionServer的缩写,它是Microsoft在COM技术方面的一个新的分支,这里不做详细说明。
----完成上面的设置以后,可以选择Finish完成工程的设置,ATL将创建相应的工程。
我们选择默认选项,不做任何改动。
3.向工程中加入一个新的ATL类:
首先通过集成环境的"
Insert"
菜单下的"
NewATLObject..."
命令进入"
ATLObjectWizard"
对话框。
如下图所示:
对话框的左边部分说明了待创建对象的基本类型,这里主要有以下几种类型:
●对象(Object):
基本的COM对象类型;
●控件(Control):
ActiveXControl类型的ATL对象;
●其他(Miscellaneous):
辅助功能,如对话框的生成等;
●数据访问(DataAccess):
数据访问,支持MTS等。
对于我们这个一般的COM服务程序,左侧选择对象(Object),右侧选择SimpleObject,点击下一步继续:
4.输入我们这个组件的类名CMyString,其他名字会自动产生,不做改动,点确定:
5.切换到Attributes(属性)标签页:
对象的属性设置是ATL对象创建过程中最复杂的部分,包括以下几个主要部分:
对象的线程模型(ThreadModel):
对象的线程模型是COM对象在多线程环境下被访问时对访问方式的控制,缺省情况下在ATL中采用的是套间模型Apartment,由系统通过消息队列方式提供并发控制。
对象的接口模型(Interface):
COM对象的接口可以是双接口(DualInterface)。
双接口不同于普通接口(CustomInterface)之处在于双接口是从Automation基本接口IDispatch继承的,而普通接口是从IUnknown接口直接继承来的。
缺省的接口模型是双接口。
对象的聚合模型(Aggregate):
COM规范不允许对象的实现继承,但是可以通过聚合方式重用其他的COM对象。
ATL对象属性设置中的聚合模型可以指定待创建的COM对象是否支持聚合模型。
缺省的选项是支持对象的聚合。
对象对错误处理的支持(SupportISupportErrorInfo):
选取这个选项可以在对象的运行过程中支持错误处理。
缺省情况下这个选项不被选中。
对象对连接点的支持(SupportConnectionPoints):
连接点是COM对象的事件机制。
选中这个选项可以使待创建的COM对象具有发出事件的能力。
缺省情况下该选项不被选中。
对象对自由线程调度的支持(FreeThreadMarshaller,简称FTM):
对象的自由线程调度是对象在处于自由线程模型状态下,为了简化对象的访问过程而采用的一种优化策略。
这里我们也使用默认选项,确定。
此时在“ClassView”标签页会增加一个CCMyString类和一个ICMyString接口。
6.为ICMyString接口添加一个方法StringTransfer(),其原型我们定义如下:
HRESULTStringTransfer([in]BSTRInData,[out]BSTR*OutData,[out,retval]long*result);
其中,[in]表示参数方向是输入;
[out]表示参数方向是输出;
[out,retval]表示参数方向是输出,同时可以作为函数运算结果的返回值。
一个函数中,可以有多个[in]、[out],但[retval]只能有一个,并且要和[out]组合后在最后一个位置。
在CmyString.cpp中,给添加的方法加入如下代码:
CComBSTRstrSource=InData;
CComBSTRtempSource=InData;
boolbNeedToUpper=true;
intj=(int)strSource.Length()-1;
//实现字符串到大写字母的转换并进行逆序转换
for(inti=0;
i<
(int)strSource.Length();
i++)
{
if(strSource[i]>
='
a'
&
&
strSource[i]<
z'
bNeedToUpper)
{
strSource[i]=strSource[i]-32;
bNeedToUpper=true;
}
if(strSource[i]=='
'
)bNeedToUpper=false;
tempSource[j]=strSource[i];
j=j-1;
}
*OutData=tempSource;
return*result;
编译链接,通过。
COM组件开发完毕。
检查生成的文件,发现系统在Debug目录下生成了一个动态链接库文件AtlComSample.dll,在工程目录下生成了一个类型库文件AtlComSample.tlb。
这2个文件就是用ATL开COM组件的成果。
你现在就可以发布这2个文件给另外的COM使用者了。
其中的DLL文件就是COM组件,使用之前必须使用RegSrv32.exe进行注册。
TLB文件是提供给开发者使用的,在后面使用COM组件的例子中我们会看到是如何使用该TLB文件的。
直接在DOS执行RegSvr32命令,得到如下的用法提示:
注册一个COM组件的命令:
regsvr32c:
\aa\xyz.dll
取消注册COM组件的命令:
regsvr32/uc:
注册一个组件或者取消注册一个组件,都会在注册表中有所反应。
其实,编译之后,VC已经在背后帮我们自动注册好了该COM组件。
使用注册表编辑器RegEdit.exe查找AtlComSample,可以发现我们这个组件已经被注册好了,见下图:
注意其中CurVer下的一个字符串:
“AtlComSample.CmyString.1”,它是我们引用这个组件的ProgID。
下面我们在VC中来使用这个组件。
6.另外新建一个MFC对话框工程MycomClient,并添加工程到当前工作区中:
7.使用MFC类向导将上个工程中开发好的组件AtlComSample添加到当前工程中:
类向导AddClassFromatypelibrary选择AtlComSample.tlb文件打开。
之后,你会发现在ClassView标签页下多了一个类:
ICMyString。
这个就是导入进来的COM组件的类,其中包含了我们在上个工程中所写的StringTransfer函数。
同时在项目目录下多了2个文件:
AtlComSample.h和AtlComSample.cpp。
下面我们就可以使用它了。
8.设计如下对话框界面:
关联如下变量:
对“字符串转换”按钮添加如下响应代码:
#include"
atlcomsample.h"
//别忘了加COM组件的头文件
voidCMyComClientDlg:
:
OnBtnTransfer()
{
ICMyStringm_MyString;
//创建COM对象:
这里的字符串一定要和注册表中的ProgID一致!
if(!
m_MyString.CreateDispatch("
AtlComSample.CmyString.1"
))
MessageBox("
创建COM组件失败:
请检查ProgID是否正确,组件是否已经注册"
);
UpdateData(true);
//得到源字符串放到m_SourceString中
BSTRbstrDestStr=SysAllocString(L"
"
m_MyString.StringTransfer(m_SourceString,&
bstrDestStr);
//调用COM接口
m_DestString=bstrDestStr;
//得到转换后结果字符串
UpdateData(false);
//将结果字符串显示到编辑框中
}
运行结果如下:
总结:
1、用ATL开发COM组件:
作业:
1、参考上例,使用ATL建立一个COM组件接口MiniStringCom,实现2个接口函数:
First2Upper“将给定字符串首字母大写化”,MergeString“将给定的2个字符串合并成1个字符串”,并在客户程序中进行使用。
2、选修:
在MSDN中查找“ATLTutorial”,即可找到微软提供的用ATL开发COM组件的教程。
按照该教程中的7个步骤,实现一个用多边形逼近圆的图形算法。
有时间则翻译并讲解。
3、选修:
阅读文章《COM组件设计与应用.mht》