1、 软件重用是业界追求的目标,人们一直希望能够像搭积木一样随意“ 装配”应用程序,组件对象就充当了积木的角色。所谓组件对象,实际上就是预定义好的、能完成一定功能的服务或接口。问题是,这些组件对象如何与应用程序、如何与其他组件对象共存并相互通信和交互?这就需要制定? 个规范,让这些组件对象按统一的标准方式工作。COM 是个二进制规范,它与源代码无关。这样,即使 COM 对象由不同的编程语言创建,运行在不同的进程空间和不同的操作系统平台,这些对象也能相互通信。COM 既是规范,也是实现,它以 COM 库(OLE32.dll 和贴OLEAut32.dll)的形式提供了访问 COM 对象核心功能的标准接
2、口以及一组 API函数,这些 API 函数用于创建和管理 COM 对象。COM 本质上仍然是客户服务器模式。客户(通常是应用程序)请求创建 COM 对象并通过 COM 对象的接口操纵 COM 对象。服务器根据客户的请求创建并管理 COM 对象。客户和服务器这两种角色并不是绝对的。 组件对象与一般意义上的对象既相似也有区别。一般意义上的对象是一种把数据和操纵数据的方法封装在一起的数据类型的实例,而组件对象则使用接口(Interface) 而不是方法来描述自己并提供服务。所谓接口,其精确定义是“基于对象的一组语义上相关的功能”,实际上是一个纯虚类,真正实现接口的是接口对象)(Interface O
3、bject)。一个 COM 对象可以只有一个接口,例如 Wndows 9598 外壳扩展;也可以有许多接口,例如 ActiveX 控件一般就有多个接口,客户可以从很多方面来操纵 ActiveX 控件。接口是客户与服务器通信的唯一途径。如果一个组件对象有多个接口,则通过一个接口不能直接访问其他接口。但是,COM 允许客户调用 COM 库中的 QueryInterface()去查询组件对象所支持的其他接口。从这个意义上讲,组件对象有点像接口对象的经纪人。在调用 QueryInterface()后,如果组件对象正好支持要查询的接口,则QueryInterface()将返回该接口的指针。如果组件对象不
4、支持该接口,则QueryInterface()将返回一个出错信息。所以,QueryInterface()是很有用的,它可以动态了解组件对象所支持的接口。接口是团向对象编程思想的一种体现,它隐藏了 COM 对象实现服务的细节。COM 对象可以完全独立于访问它的客户,只要接口本身保持不变即可。如果需要更新接口,则可以重新定义一个新的接口,对于使用老接口的客户来说,代码得到了最大程度的保护。Delphi 通过向导可以非常迅速和方便的直接建立实现 COM 对象的代码,但是整个 COM 实现的过程被完全的封装,甚至没有 VCL 那么结构清晰可见。一个没有 C+下 COM 开发经验甚至没有接触过 COM
5、开发的 Delphi 程序员,也能够很容易的按照教程设计一个接口,但是,恐怕深入一想,连生成的代码代表何种意义,哪些能够定制都不清楚。前几期 “DELPHI 下的 COM 编程技术”一文已经初步介绍了 COM 的一些基本概念,我则想谈一些个人的理解,希望能给对 Delphi 下 COM 编程有疑惑的朋友带来帮助。COM (组件对象模型 Component Object Model)是一个很庞大的体系。简单来说,COM 定义了一组 API 与一个二进制的标准,让来自不同平台、不同开发语言的独立对象之间进行通信。COM 对象只有方法和属性,并包含一个或多个接口。这些接口实现了 COM 对象的功能,
6、通过调用注册的 COM 对象的接口,能够在不同平台间传递数据。COM 光标准和细节就可以出几本大书。这里避重就轻,仅仅初步的解释Delphi 如何进行 COM 的封装及实现。对于上述 COM 技术经验不足的 Delphi程序开发者来说,Delphi 通过模版生成的代码就像是给你一幅抽象画照着画一样,画出来了却不一定知道画的究竟是什么,也不知该如何下手画自己的东西。本文能够帮助你解决这类疑惑。再次讲解一些概念“DELPHI 下的 COM 编程技术”一文已经介绍了不少 COM 的概念,比如GUID、CLSID、IID,引用计数, IUnKnown 接口等,下面再补充一些相关内容:COM 与 DCO
7、M、COM+、OLE、ActiveX 的关系DCOM(分布式 COM)提供一种网络上访问其他机器的手段,是 COM 的网络化扩展,可以远程创建及调用。COM+是 Microsoft 对 COM 进行了重要的更新后推出的技术,但它不简单等于 COM 的升级,COM+是向后兼容的,但在某些程度上具有和 COM 不同的特性,比如无状态的、事务控制、安全控制等等。以前的 OLE 是用来描述建立在 COM 体系结构基础上的一整套技术,现在 OLE仅仅是指与对象连接及嵌入有关的技术;ActiveX 则用来描述建立在 COM 基础上的非 COM 技术,它的重要内容是自动化(Automation),自动化允许
8、一个应用程序(称为自动化控制器)操纵另一个应用程序或库(称为自动化服务器)的对象,或者把应用程序元素暴露出来。由此可见 COM 与以上的几种技术的关系,并且它们都是为了让对象能够跨开发工具跨平台甚至跨网络的被使用。Delphi 下的接口Delphi 中的接口概念类似 C+中的纯虚类,又由于 Delphi 的类是单继承模式(C+是多继承的),即一个类只能有一个父类。接口在某种程度上可以实现多继承。接口类的声明与一般类声明的不同是,它可以象多重继承那样,类名 = class (接口类 1,接口类 2 ),然后被声明的接口类则重载继承类的虚方法,来实现接口的功能。以下是 IInterface、IUn
9、known、IDispatch 的声明,大家看出这几个重要接口之间是什么样的联系了吗?任何一个 COM 对象的接口,最终都是从 IUnknown继承的,而 Automation 对象,则还要包含 IDispatch,后面 DCOM 部分我们会看到它的作用。IInterface = interface00000000-0000-0000-C000-000000000046function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;function _AddRef: Integer; stdcall;function _
10、Release: Integer; stdcall;end;IUnknown = IInterface;IDispatch = interface(IUnknown)00020400-0000-0000-C000-000000000046function GetTypeInfoCount(out Count: Integer): HResult; stdcall;function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;function GetIDsOfNames(const IID: TGUI
11、D; Names: Pointer;NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;end;对照“DELPHI 下的 COM 编程技术”一文,可以明白 IInterface 中的定义,即接口查询及引用记数,这也是访
12、问和调用一个接口所必须的。QueryInterface 可以得到接口句柄,而 AddRef 与 Release 则负责登记调用次数。COM 和接口的关系又是什么呢?COM 通过接口进行组件、应用程序、客户和服务器之间的通信。COM 对象需要注册,而一个 GUID 则是作为识别接口的唯一名字。假如你创建了一个 COM 对象,它的声明类似 Txxxx= class(TComObject, Ixxxx),前面是 COM 对象的基类,后面这个接口的声明则是: Ixxxx = interface(IUnknown) 。所以说 IUnknown 是 Delphi 中 COM 对象接口类的祖先。到这一步,我
13、想大家对接口类的来历已经有初步了解了。聚合接口是 COM 实现的基础,接口也是可继承的,但是接口并没有实现自己,仅仅只有声明。那么怎么使 COM 对象对接口的实现得到重用呢?答案就是聚合。聚合就是一个包含对象(外部对象)创建一个被包含对象(内部对象),这样内部对象的接口就暴露给外部对象。简单来说,COM 对象被注册后,可以找到并调用接口。但接口不是仅仅有个定义吗,它必然通过某种方式找到这个定义的实现,即接口的“实现类”的方法,这样才最终通过外部的接口转入进行具体的操作,并通过接口返回执行结果。进程内与进程外(In-Process, Out-Process)进程内的接口的实现基础是一个 DLL,
14、进程外的接口则是建立在应用程序(EXE)上的。通常我们建立进程外接口的目的主要是为了方便调试(跟踪DLL 是件很麻烦的事),然后在将代码改为进程内发布。因为进程内比进程外的执行效率会高一些。(也就是先建立进程内的接口,再将其改为进程内发布。)COM 对象创建在服务器的进程空间。如果是 EXE 型服务器,那么服务器和客户端不在同一进程;如果是 DLL 型服务器,则服务器和客户端就是一个进程。所以进程内还能节省内存空间,并且减少创建实例的时间。StdCall 与 SafeCallDelphi 生成的 COM 接口默认的方法函数调用方式是 stdcall 而不是缺省的Register。这是为了保证不
15、同语言编译器的接口兼容。双重接口(在后面讲解自动化时会提到双重接口)中则默认的是 SafeCall。它的意义除了按 SafeCall 约定方式调用外,还将封装方法以便向调用者返回HResult 值。SafeCall 的好处是能够捕获所有异常,即使是方法中未被代码处理的异常,也可以被外套处理并通过 HResult 返回给调用者。WideString 等一些有差异的类型接口定义中缺省的字符参数或返回值将不再是 String 而是WideString。WideString 是 Delphi 中符合 OLE 32-bit 版本的 Unicode 类型,当是字符时,WideString 与 String
16、 几乎等同,当处理 Unicode 字符时,则会有很大差别。联想到 COM 本身是为了跨平台使用,可以很容易的理解为什么数据通信时需要使用 WideString 类型。同样的道理,integer 类型将变成 SYSINT 或者 Int64、SmallInt 或者 Shortint,这些细微的变化都是为了符合规范。通过向导生成基础代码打开创建新工程向导(菜单“File-New-Other”或“New Items 按钮”),选择ActiveX 页。先建立一个 ActiveX Library。编译后即是个 DLL 文件(进程内)。然后在同样的页面再建立一个 COM Object。接着你将看到如下向导
17、,除了填写类名外(接口名会自动根据类名填充),创建有实例模式(Instancing)和线程模式(Threading Model )的选项。实例模式-决定客户端请求后,COM 对象如何创建实例:Internal:供 COM 对象内部使用,不会响应客户端请求,只能通过 COM 对象内部的其他方法来建立;Single Instance:不论当前系统内部是否存在相同 COM 对象,都会建立一个新的程序及独立的对象实例;Mulitple Instance:如果有多个相同的 COM 对象,只会建立一个程序,多个COM 对象的实例共享公共代码,并拥有自己的数据空间。Single/ Mulitple Inst
18、ance 有各自的优点,Mulitple 虽然节省了内存但更加费时。即 Single 模式需要更多的内存资源,而 Mulitple 模式需要更多的 CPU 资源,且 Single 的实例响应请求的负荷较为平均。该参数应根据服务器的实际需求来考虑。线程模式有五种:Single:仅单线程,处理简单,吞吐量最低;Apartment:COM 程序多线程, COM 对象处理请求单线程;Free:一个 COM 对象的多个实例可以同时运行。吞吐量提高的同时,也要求对 COM 对象进行必要的保护,以避免多个实例冲突;Both:同时支持 Aartment 和 Free 两种线程模式。Neutral:只能在 CO
19、M+下使用。虽然 Free 和 Both 的效率得到提高,但是要求较高的技巧以避免冲突(这是很不容易调试的),所以一般建议使用 Delphi 的缺省方式 。类型库编辑器(Type Library)假设我们建立一个叫做 TSample 的类和 ISample 的接口(如图),然后使用类型库编辑器创建一个方法 GetCOMInfo(在右边树部分点击右键弹出菜单选择 New-Method 或者点击上方按钮),并于左边 Parameters 页面建立两个参数(ValInt : Integer , ValStr : String),返回值为 BSTR。如图:可以看到,除了常用类型外,参数和返回值还可以支
20、持很多指针、OLE 对象、接口类型。建立普通的 COM 对象,其 Returen Type 是可以任意的,这是和 DCOM 的一个区别。双击 Modifier 列弹出窗口,可以选择参数的方式:in、out 分别对应 const、out定义,选择 Has Default Value 可设置参数缺省值。Delphi 生成代码详解 点击刷新按钮刷新后,上面类型库编辑器对应的 Delphi 自动生成的代码如下:unit uCOM;$WARN SYMBOL_PLATFORM OFFinterfaceusesWindows, ActiveX, Classes, ComObj, pCOM_TLB, StdV
21、cl;typeTSample = class(TTypedComObject, ISample)protectedfunction GetCOMInfo(ValInt: SYSINT; const ValStr: WideString): WideString;stdcall;end;implementationuses ComServ; /竟然会自动加上这个东西function TSample.GetCOMInfo(ValInt: SYSINT;const ValStr: WideString): WideString;beginend;initializationTTypedComObje
22、ctFactory.Create(ComServer, TSample, Class_Sample,ciMultiInstance, tmApartment);end.引用单元有三个特殊的单元被引用:ComObj,ComServ 和 pCOM_TLB。ComObj 里定义了 COM 接口类的父类 TTypedComObject 和类工厂类 TTypedComObjectFactory(分别从 TComObject 和TComObjectFactory 继承 加了 Typed,早期版本如 Delphi4 建立的 COM,就直接从 TcomObject 继承和使用 TComObjectFactor
23、y 了); ComServ 单元里面定义了全局变量 ComServer: TComServer真的有这个 ,它是从TComServerObject 继承的,关于这个变量的作用,后面将会提到。这几个类都是 delphi 实现 COM 对象的比较基础的类,TComObject(COM 对象类)和 TComObjectFactory(COM 对象类工厂类)本身就是 IUnknown 的两个实现类,包含了一个 COM 对象的建立、查询、登记、注册等方面的代码。TComServerObject 则用来注册一个 COM 对象的服务信息。接口定义说明再看接口类定义 TSample = class(TType
24、dComObject, ISample)。到这里,已经可以通过涉及的父类的作用大致猜测到 TSample 是如何创建并注册为一个标准的 COM 对象的了。那么接口 ISample 又是怎么来的呢?pCOM_TLB 单元是系统自动建立的 ,其名称加上了_TLB,它里面包含了ISample = interface(IUnknown)的接口定义。前面提到过,所有 COM 接口都是从IUnknown 继承的。在这个单元里我们还可以看到三种 ID(类型库 ID、IID 及 COM 注册所必须的CLSID)的定义:LIBID_pCOM,IID_ISample 和 CLASS_Sample。关键是这时接口本
25、身仅仅只有定义代码而没有任何的实现代码,那接口创建又是在何处执行的?_TLB 单元里还有这样的代码:CoSample = classclass function Create: ISample;class function CreateRemote(const MachineName: string): ISample;end;class function CoSample.Create: ISample;beginResult := CreateComObject(CLASS_Sample) as ISample;end;class function CoSample.CreateRemot
26、e(const MachineName: string): ISample;beginResult := CreateRemoteComObject(MachineName, CLASS_Sample) as ISample;end;由 Delphi 的向导和类型编辑器帮助生成的接口定义代码,都会绑定一个“Co+ 类名”的类,它实现了创建接口实例的代码。CreateComObject 和 CreateRemoteComObject函数在 ComObj 单元定义,它们就是 使用 CLSID 创建 COM/DCOM 对象的函数!初始化:注册 COM 对象的类工厂类工厂负责接口类的统一管理实际上是由
27、支持 IClassFactory 接口的对象来管理的。类工厂类的继承关系如下:IClassFactory = interface(IUnknown) TComObjectFactory=class(TObject,IUnknown,IClassFactory,IClassFactory2) TTypedComObjectFactory = class(TComObjectFactory)我们知道了接口 ISample 是怎样被创建的,接口实现类 TSample 又是如何被定义为 COM 对象的实现类。现在解释它是怎么被注册,以及何时创建的。这一切的小把戏都在最后 Delphi 单元里的 ini
28、tialization 的部分,这里有一条类工厂建立的语句。Initialization 是 Delphi 用于初始化的特殊部分,此部分的代码将在整个程序启动的时候首先执行。回顾前面的内容并观察一下 TTypedComObjectFactory 的参数:【ComServer 是用于注册/ 撤消注册 COM 服务的对象,TSample 是接口实现类,Class_Sample 是接口唯一对应的 GUID,ciMultiInstance 是实例模式,tmApartment 是线程模式。一个 COM 对象应该具备的特征和要素都包含在了里面!】那么 COM 对象的管理又是怎么实现的呢?在 ComObj
29、单元里面可以见到一条定义 function ComClassManager: TComClassManager;这里 TComClassManager 顾名思义就是 COM 对象的管理类。任何一个祖先类为 TComObjectFactory 的对象被建立时,其 Create 里面会执行这样一句:ComClassManager.AddObjectFactory(Self);AddObjectFactory 方法的原形为 procedure TComClassManager.AddObjectFactory(Factory: TComObjectFactory);相对应的还有RemoveObjec
30、tFactory 方法。具体的代码我就不贴出来了,相信大家已经猜测到了它的作用将当前对象(self)加入到 ComClassManager 管理的对象链(FFactoryList)中。封装的秘密读者应该还有最后一个疑问:假如服务器通过类工厂的注册以及 GUID 确定一个 COM 对象,那当客户端调用的时候,服务器是如何启动包含 COM 对象的程序的呢?当你建立 ActiveX Library 的工程的时候,将发现一个和普通 DLL 模版不同的地方它定义了四个输出例程:exportsDllGetClassObject,DllCanUnloadNow,DllRegisterServer,DllUn
31、registerServer;这四个例程并不是我们编写的,它们都在 ComServ 单元例实现。单元还定义了类 TComServer,并且在初始化部分创建了类的实例,即前面提到过的全局变量 ComServer。例程 DllGetClassObject 通过 CLSID 得到支持 IClassFactory 接口的对象;例程 DllCanUnloadNow 判断 DLL 是否可从内存卸载; DllRegisterServer和 DllUnregisterServer 负责 DLL 的注册和解除注册,其具体的功能由ComServer 实现。接口类的具体实现好了,现在自动生成代码的来龙去脉已经解释清
32、楚了,下一步就是由我们来添加接口方法的实现代码。在 function TSample.GetCOMInfo 的部分添加如下代码。我写的例子很简单,仅仅是根据传递的参数组织一条字符串并返回。以此证明接口正确调用并执行了该代码:function TSample.GetCOMInfo(ValInt: SYSINT;const ValStr: WideString): WideString;constServer1 = 1; Server2 = 2; Server3 = 3;vars : string;begins := This is COM server : ;case ValInt ofServ
33、er1: s := s + Server1;Server2: s := s + Server2;Server3: s := s + Server3;end;s := s + #13 + #10 + Execute client is + ValStr;Result := s;end;注册、创建 COM 对象及调用接口随便建立一个 Application 用于测试上面的 COM。必要的代码很少,创建一个接口的实例然后执行它的方法。当然我们得先行注册 COM,否则调用根据 CLSID 找不接口的话,将报告“无法向注册表写入项”。如果接口定义不一致,则会报告“Interface not suppor
34、ted”。编译上面的这个 COM 工程,然后选择菜单“Run Register ActiveX Server”,或者通过 Windows 下 system/system32 目录中的 regsvr32.exe 程序注册编译好的 DLL 文件。regsvr32 的具体参数可以通过 regsvr32/?来获得。对于进程外(EXE 型)的 COM 对象,执行一次应用程序就注册了。提示 DLL 注册成功后,就应该可以正确执行下列客户端程序了:uses ComObj, pCOM_TLB;procedure Ttest.Button1Click(Sender: TObject);varCOMSvr : I
35、Sample;retStr : string;beginCOMSvr := CreateComObject(CLASS_Sample) as ISample;if COMSvr New-Other-ActiveX Library 标签 下的 Transactional Object 2: 然后填写 : CoClss Name :类的名字,比如:ComPlus Threading Modal :线程模式:Apartment 选项 : Supports transactions 3:然后在 View-Library 的对话框中增加方法 注意: 如果参数为输出的话,则类型要是指针型,比如:Long
36、* ,然后修改后面的参数 in :out,ret 4:最后完善增加的方法就 ok 了2:客户端调用的编写: 1:先倒入 Com+的接口类型. Project -import Type Library-选中你编写的 Com+的类型,然后选择 :Create Unit 3、安装 COM+组件有两种方式,第一种( 推荐):如果是在 IDE 环境里,点击“Run-Install COM+ Objects”即可把打开的 Active Library 项目安装到 COM+环境中,注意:如果打开的项目是 一个普通的 Application 项目,是不能被安装到 COM+环境中的。 将要安装的 com+打上勾
37、 ,然后在 application 中有两个选项:install to existing application :表示你的 com+安装在 com 服务器的哪个组件包中, install to New application:表示将当前 com+组件安装到一个新的组件包中. 第二种办法:打开控制面板- 管理工具-组件服务 -计算机-我的电脑-COM+应用程序,在 COM+应用程序的树项上点击 鼠标右键,选择“新建-应用程序”-创建一个空的应用程序,并为此应用程序命名,接下 来点击“ 下一步”直到结束即可。建立了空的 COM+应用程序后,接下来就是把 COM DLL 安装 到 COM+应用程序
38、中了。在刚建立的空应用程序的树项中新建一个组件,选择“安装新组件 ”, 在打开文件对话框中选择要安装到 COM+环境中的 DLL 文件,之后跟着向导做都可以了,要把 多个 COM DLL 安装到同一个 COM+应用程序包中,只需重复以上步骤即可。4、“导出客户端组件包”指的是把已经注册的组件导出为 .msi 格式的文件,这些文件在客 户端安装后,只会在客户端注册组件,而不会安装多余的文件。如果不在客户端注册组件, 是不不能调用位于服务器上的组件的(此指服务器和客户端分布在不同的机器上时)。5:调试 Com+程序 -ok1.打开 Windows 中的组件管理,找到要调试的组件包 ,点右键, 选择
39、属性,在高级这页里选择调试选项,打勾; 然后在下面的调试路径中找到/processID:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx 复制出来 2.在 dephi 中 Run | Parameters HOST APPLICATION 填入 系统路径system32dllhost.exe PARAMETERS 粘巾 /processID:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx 3。很关键的一点:组件程序:project|option|linker|Include TD32 debug info 和 Include remote debug symbols 打勾
40、 4.启动 delphi,运行要调试的 Com+程序,设置断点, 然后运行客户端程序即可进入到 Com+断点. 5.调试完后记得要在 Windows 中的组件管理中的高级这页里调试选项勾去掉哟. 6:Com+需要注意的地方 : 1:客户机运行就会报 interface not supported 错误 大致原因: 轻舞肥羊 (2004-05-09 11:00:01) COM+的权限依赖于 Windows 的权限配置, 在服务器需要有客户机的用户名和密码。 如果还不行,就在服务器上重新安装 com+,重新导出.2:建立工程时,com+不能包含在工程组中.(我的实践 ) 3:COM+不支持 Ora
41、cle 吗?在用事务的时候出错 :Using Oracle with Microsoft Transaction Server and COM+ 7:在 Com+中添加远程数据模块 1:File-New-Other-Multitier 标签 下的 Transactional Data Module 2:然后在 View-Library 的对话框中增加方法. 8:Com+中传递数组 先定义数组: type TDataRecord = record A: Byte; B: LongWord; C: Word;D: LongWord; end; 1:server: function GetData:
42、 OleVariant; var P: Pointer; begin VarClear(Result);/不知 D5 有没。 try Result := VarArrayCreate(0, SizeOf(TDataType), varByte); P := VarArrayLock(Result); /Data: TDataType 为你要传的记录类型 Move(Data, P, SizeOf(TDataType); finally VarArrayUnLock(Result); end; end;2:Client procedure GetFile(const FileName: strin
43、g); var P: Pointer; V: OleVariant; Data: TDataType; begin FillChar(Data, SizeOf(Data), 0); V := SocketConnection1.AppServer.GetData; try P := VarArrayLock(V); Move(P, Data, SizeOf(Data); finally VarArrayUnLock(V); end; end;9:Com+中传递记录集 下面 xeen 实验成功 uses ADOInt* 可以;你可以将 ADO 的数据作为一个 Variant 类型的变量进行传送:
44、adodataset1.RecordSet 这是原生的 ado 数据 这是服务端的一个方法的代码:把 CodeSet 的类型改为 Variant*in,out function TADORec.getData: OleVariant; begin AdoDataSet1.Open; result := adodataset1.RecordSet; end; *客户端 uses adoint; var MyRecordset :_recordset; begin MyRecordset := IUnknown(CodeSet) as _recordset * Com 基本概念: 1:COM 是一
45、个基于二进制的标准。打个比方,我们用 Delphi 实现了一个对象,一般情况下,我们只能在 Delphi 来生成这个对象的实例并调用,而如果我们用 Delphi 实现了一个 COM 对象的话,我们可以用 VC、VB 或者其他任何一种支持 COM 对象的语言来生成实例和调用。反过来也一样,我们可以在Delphi 中使用各种 COM 对象,而不用介意它是用什么语言编写的。COM 提供了分布式 COM 对象的机制,形象地说你可以调用另一台机器中的 COM 对象。COM+则是 MTS 的一个升级,在 COM 的基础上进一步提供了事务处理和其他很多 Pool 技术。 2:线程模式:Apartment:多
46、个线程服务. 3:当建立 Com+时选择的事务模式为 Requires a Transaction,Com+会根据客户的的请求建立相应的事务,不仅仅时数据库,还会有系统资源等事务.成功 SetComplete.回滚 SetAbort. 选择 Requires a Transaction 表示当用户调用这个 COM+组件时,COM+环境会为这个组件建立一个新的事务上下文,这和数据库的事务不是一回事。当你的 COM+组件提交数据时如果出错,应该告诉事务上下文,只要调用 COM+组件的 SetAbort 方法就可以。这样一来,处于同一个事务上下文的所有 COM+组件都会 Rollback。如果数据提
47、交成功,应该调用 SetComplete,不调用这个方法也可以,因为在默认情况下, COM+组件的事务状态设置为 EnableCommite。当处于同一事务上下文的所有 COM+组件对象都调用了 SetComplete 时,该事务上下文才会真正的向数据库提交数据。 4:SetAbort 合 SetComplete 是否正确调用 5:(阿朱) 建议:多个 DLL 在一个包,一个 DLL 中的 COM 公用一个ADOCONNECTION 6:问题:我已经在 Transactional Data Module 的 Pooled 属性里面设置了True 了,但是在 Win2000 的组件服务管理的“组
48、件属性”的“激活”一栏里面,仍然无法打开“启用对象共用”的选项 -您可以将线程模式设置为 tmNeutral 或者tmBoth 都可以。Com 有需要研究和有疑问的地方: 1:Com+的模式: 2:到 Com+的资源 Pooling 机制 3:用 delphi6 开发 Com+,用 Neutral 模式,尽量用 Object Pooling,当然就是要无状态了,事务要尽量短,避免死锁,用 ADO 不要用 BDe.Dbexpress等. 4:我的做法是,按功能划分组件,把查询和更新分为两个功能组件,因为查询不需要事务,所以只要支持事务就行了。更新一般需要事务 5:COM+的事务属性 COM+的事务默认级别是序列 Read,而不是我们认为的 commit Read,而且我们设置 AdoConnection 的隔离级是不 管用的,只能强制用显式的 SQL 才能起效果。 例如: 一个中间层 COM select * from a where py_code like S% 继续下面有很多代码 另一个中间层 COM update a set py_code