Delphi 编写COM+组件的知识和样例

 一、COM组件的基础知识介绍:

1、什么是COM

         COM是微软定的一种组件编写的规范,目的是为了实现组件的重用,不管你是用C、DELPHI、VB什么语言写的,只要遵守这种规范就是COM组件,就能相互之间调用。那这个规范是什么呢,最简单的,就是按规定使用接口,微软定了个IUnkown接口,只要实现了这个接口的类,就可以称为COM组件。在此基础上可以加上自己的接口,实现特定的功能,这就成了有用的COM组件了。

        

2、COM、DCOM、COM+

         COM是一个基于二进制的标准。打个比方,我们用Delphi实现了一个对象,一般情况下,我们只能在Delphi来生成这个对象的实例并调用,而如果我们用Delphi实现了一个COM对象的话,我们可以用VC、VB或者其他任何一种支持COM对象的语言来生成实例和调用。反过来也一样,我们可以在Delphi中使用各种COM对象,而不用介意它是用什么语言编写的。COM提供了分布式COM对象的机制,形象地说你可以调用另一台机器中的COM对象。COM+则是MTS的一个升级,在COM的基础上进一步提供了事务处理和其他很多Pool技术。

 

2、DELPHI中如何创建COM组件(两个步骤)

  (1)、创建一个ActiveX Library,这是个DLL;【ActiveX选项卡】

   (2)、创建一个COM Object或者Automation Object,二者选其一,这两者都是COM组件,但是当然还是有区别的,差在调用的方法上,这个后面再说。【ActiveX选项卡】;

(创建了一个ActiveX Library,名叫mylib。一个Automation Object,名叫myobj。并且编译注册。)

  注意:COM Object只能通过接口调用,Automation Object可以调接口或名称调用。

        

3、DELPHI中如何调用COM组件(两种方法调用),

第一种方法(用接口来调用)
        适用于支持接口的语言,如C、DELPHI;VB就不行了,好处是速度快,因为不用根据名称去查找了,还有开发时编译器能检查,缺点是要导入类型库。

需要如下操作:

        在use里加上mylib_TLB类型库(如果不是自己开发的没有TLB可以从DLL导入生成的),代码是var myobj:Imyobj;myobj=Comyobj.create();之后就可以用了。如果有兴趣可以看看类型库的代码里,Comyobj.create里调用了CreateComObject,这函数返回的是IUnkown接口,再用as操作转成你需要的Imyobj接口。

 

第二种方法(用名称来调用)
        适用于所有的语言,好处是不用导入类型了,缺点是调用的速度慢,还有开发时编译器不检查。

        用第二种方法不需要类型库了,代码是var myobj:variant;myobj=CreateOleObject('mylib.myobj');之后就可以用了,这个方便吧。其实为了实现用名字调用,需要加上IDispatch接口,不过delphi都自动给我们加好了,不用管直接用就行。

1>、 idispatch是COM object的接口, 在Delphi中通常指一个OleObject.

2>、 OleVariant是一种COM object兼容的Variant类型, 可以通用任何Ole Automation 类型, 他与CreateOleObject创建的idispatch兼容 。

(COM组件的编写和COM+组件的编写差不多,所以只写COM+组件的编写情况)

 

二、COM+编写的具体实例:

1、Com+的编写:

   1>、File---->New---->Other....---->ActiveX Library 标签下的 Transactional Object

   2>、然后填写: CoClss Name :类的名字,比如:ComPlusThreading Modal :线程模式:Apartment 选项: Supports transactions

         当建立COM+ 时选择的事务模式为Requires a Transaction, COM+会根据客户的的请求建立相应的事务,不仅仅是数据库,还会有系统资源等事务.成功SetComplete.回滚SetAbort. 选择Requires a Transaction表示当用户调用这个COM+组件时,COM+环境会为这个组件建立一个新的事务上下文,这和数据库的事务不是一回事。当你的COM+组件提交数据时如果出错,应该告诉事务上下文,只要调用COM+组件的SetAbort方法就可以。这样一来,处于同一个事务上下文的所有COM+组件都会Rollback。如果数据提交成功,应该调用SetComplete,不调用这个方法也可以,因为在默认情况下,COM+组件的事务状态设置为EnableCommite。当处于同一事务上下文的所有COM+组件对象都调用了SetComplete时,该事务上下文才会真正的向数据库提交数据。

   3>、然后在View--->Library的对话框中增加方法 注意:如果参数为输出的话,则类型要是指针型,比如:Long * ,然后修改后面的参数in :out,ret

   4>、最后完善增加的方法就ok了

 

2、客户端调用的编写:

   1>、先倒入Com+的接口类型. Project --->import Type Library---->选中你编写的Com+的类型,然后选择:Create Unit

 

3、安装COM+组件有两种方式,

   1>、第一种(推荐):如果是在IDE环境里,点击“Run->Install COM+ Objects”即可把打开的Active Library项目安装到COM+环境中,注意:如果打开的项目是 一个普通的Application项目,是不能被安装到COM+环境中的。 将要安装的com+打上勾,然后在application中有两个选项:install to existing application :表示你的com+安装在com服务器的哪个组件包中, install to New application:表示将当前com+组件安装到一个新的组件包中.

   2>、第二种办法:打开控制面板-> 管理工具->组件服务->计算机->我的电脑->COM+应用程序,在COM+应用程序的树项上点击 鼠标右键,选择“新建->应用程序”->创建一个空的应用程序,并为此应用程序命名,接下来点击“下一步”直到结束即可。建立了空的COM+应用程序后,接下来就是把COM DLL安装 到COM+应用程序中了。在刚建立的空应用程序的树项中新建一个组件,选择“安装新组件”, 在打开文件对话框中选择要安装到COM+环境中的DLL文件,之后跟着向导做都可以了,要把多个COM DLL安装到同一个COM+应用程序包中,只需重复以上步骤即可。

 

4、导出客户端组件包

导出客户端组件包指的是把已经注册的组件导出为.msi格式的文件,这些文件在客户端安装后,只会在客户端注册组件,而不会安装多余的文件。如果不在客户端注册组件,是不能调用位于服务器上的组件的(此指服务器和客户端分布在不同的机器上时)。

 

 5、调试Com+程序 ---ok

   1>、打开Windows中的组件管理,找到要调试的组件包,点右键,选择属性,在高级这页里选择调试选项,打勾; 然后在下面的调试路径中找到/processID:{xxxxxxxxxxxxxxxxxxxxxxxxxxxxx} 复制出来

   2>、在dephi中Run | Parameters… HOST APPLICATION 填入 {系统路径}\system32\dllhost.exe PARAMETERS 粘巾 /processID:{xxxxxxxxxxxxxxxxxxxxxxxxxxxxx}

   3>、很关键的一点:组件程序:project|option|linker|Include TD32 debug info 和Include remote debug symbols打勾

   4>、启动delphi,运行要调试的Com+程序,设置断点,然后运行客户端程序即可进入到Com+断点.

   5>、调试完后记得要在 Windows中的组件管理中的高级这页里调试选项勾去掉哟.

 

6、Com+需要注意的地方:

   1>、客户机运行就会报 interface not supported 错误.大致原因: COM+的权限依赖于Windows的权限配置,在服务器需要有客户机的用户名和密码。 如果还不行,就在服务器上重新安装com+,重新导出.

   2>、建立工程时,com+不能包含在工程组中.(我的实践)

   3>、COM+不支持Oracle吗?在用事务的时候出错:Using Oracle with Microsoft Transaction Server and COM+

 

7、在Com+中添加远程数据模块

   1>、File---->New---->Other....---->Multitier 标签 下的 Transactional Data Module

   2>、然后在View--->Library的对话框中增加方法.

 

8、Com+中传递数组

   1>、先定义数组:

     type TDataRecord = record

        A: Byte;

        B: LongWord;

        C: Word;

        D: LongWord;

     end;

   2>、服务器端:

     function GetData: 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)); // 把Data的值传给P指向的变体类型

        finally

           VarArrayUnLock(Result);

        end;

     end;

 

   3>、客户端:

     procedure GetFile(const FileName: string);

     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+中传递记录集

服务器端:

Uses  ADOInt;

你可以将ADO的数据作为一个Variant类型的变量进行传送:adodataset1.RecordSet这是原生的ado数据

 这是服务端的一个方法的代码:把CodeSet的类型改为Variant*[in,out]

function TADORec.getData: OleVariant;

begin

   AdoDataSet1.Open;

   result := adodataset1.RecordSet;

end;

//可以将ADO的数据作为一个Variant类型的变量进行传送:adodataset1.RecordSet这是原生的ado数据

            

客户端

uses adoint;

var

    MyRecordset :_recordset;

begin

    MyRecordset := IUnknown(CodeSet) as _recordset

End;

 

10、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=py_code where py_code like 'B%'; 这时这个COM将会死锁等待,虽然改动的不是一个数据也会死锁 用Commit Read 或UnCommit Read不会建议: 所以被多个子系统更新或读的表,要在最后打开和更新,不要早。另外可以强制改变ADOCONNECTION的隔离级

6、Com+中如何进行事务处理? 在COM+中,如何使用SetComplete和SetAbort进行事务管理?SetComplete和SetAbort怎么能在客户端调用呢?这样肯定是不行的。要保证事务,就在服务器端声明一个专门保存的接口方法,例如:

procedure UpdateMyData(var AData1, AData2: OleVariant; var AMaxError, AErrorCount: Integer); begin    

    try

        DataSetProvider1.Data := AData1;

        DataSetProvider1.ApplyUpdate(AMaxError, AErrorCount);

        DataSetProvider2.Data := AData2;

        DataSetProvider2.ApplyUpdate(AMaxError, AErrorCount);

        SetComplete;

    except

        SetAbort;

    end;

end;

   7、在组件管理器中的“事务列表”里也看不到事务(应该有事务的时候),怎么看?

   8、Com+的两大研究:事务和 pooling

   9、做一个提交用的COM+对象和一个协调用的COM+对象。

   10、建议:多个DLL在一个包,一个DLL中的COM公用一个ADOCONNECTION

   11、问题:我已经在Transactional Data Module的Pooled属性里面设置了True了,但是在Win2000的组件服务管理的“组件属性”的“激活”一栏里面,仍然无法打开“启用对象共用”的选项 --->您可以将线程模式设置为tmNeutral或者tmBoth都可以。


你可能感兴趣的:(object,服务器,application,dll,library,Delphi)