COM编程
进程内COM服务器(In-Process COM Server)
进程内的COM服务器(In-Process Com Server)在这前已经简单的介绍过,在此,只对一些需要明白、理解的知识点进行阐述,再进行编程可能会更好一点;进程内服务器是由于它们在DLL内实现而获得这个名称的。因此,服务器占擗了和使用它的应用程序一样的地址空间(进程)。所有的进程内COM服务器输出四个标准函数:DllRegisterServer、DllUnregisterServer、DllGetClassObject和DllCanUnloadNow。根据字面意思,我们也已经可以感觉的出这些函数的工作都会是什么。当然,Delphi已经为我们提供了这些函数的缺省实现。因此,读者不必自己写代码来实现这些函数,但应该理解他们具体是做什么的。
² DllRegisterServer。DllRegisterServer以两种方式自动调用。IDE的Register ActiveX Server菜单选项调用它,Windows的命令行应用程序RegSvr32.exe(或者Borland应用程序TRegSvr)也会调用它,很多情况下可以直接用Register ActiveX Server去调用,如果手动的去实现,可以写一个.Bat文件。无论通过那种方式调用它,DllRegisterServer用Windows注册表来注册COM对象。
² DllUnregisterServer。可以感觉的出来,这个函数和DllRegisterServer是作相反的工作,实际上它们就是一个相逆的过程,它移走了DllRegisterServer放在Windows注册表中的所有条目。可以用IDE里的 UnRegister ActiveX Server来调用这个工程。
² DllGetClassObject。DllGetClassObject负责提供给COM一个类厂,该类厂用于创建一个COM对象(在讨论类厂的时候,我们也做过明确的说明)。
² DllCanUnloadNow。COM负责调用DllCanUnLoadNow来看是否可以从内存中卸载COM服务器。
线程支持(Threading Support)
线程支持只适合于进程内服务器,并且不适用于进程外服务器。进程内服务器可以附着在几个的一个。进程内服务器的线程模型被存在Windows注册表中,具体如下:
² ……
² ……
² ……
² ……
…………
注册服务器(Registering the Server)
此处将不再重提如何注册服务器,只是简单的说一说为什么要进行注册。我们都知道,普通的Dll也需要进行注册才可以运行,而COM对象或是COM服务器要提供给服务于Client,那么客户端首先要知道要没有这个服务?如何进行这个服务的调用或是访问,因此它会找一些有用的键值,而客户所找的范畴就是注册表。由此而言,注册的确很有必要而肯是必不可少的。
构造函数
应该明确的是,做为一个提供服务的对象或接口或是一个组件,有一步工作应该提前做,那就是构造,而构造也是初始化,并且之前我们也说过,COM对象最好派生于TcomObject类,如此一来,我们就不得不去考虑在TcomObject中定义的构造函数都调用了虚方法函数Initialize。如果需要为自己的COM对象提供初化代码,只需要重载Initialize方法,定义如下:
Procedure Initialize ;Virtual;
将初始化代码放在Initialize中而不是构造函数中的原因是:Delphi中的COM对象的基类包含了一系列的非虚构造函数。根擗需要,类厂奖在不同的实例中调用不同的构造函数。而Initialize是虚方法,并且是唯一可以在调用时不考虑哪个构造函数是用于创建COM对象的方法。
创建一个进程内COM对象的实例
当用户需要在自己的客户程序代码中创建一个COM对象,一般会调用CreateConObject函数,该函数在ComObj.pas中定义,声明如下:
Function CreateComObject(const ClassID:TGUID):IunKnown;
CreateComObject将要创建的COM对象的GUID作为一个参数,并返回该COM对象的IunKnown指针。苦所请求的GUID在Windows注册表中找不到的话,将会抛出一个异常。那么我们一起来分析一下CreateComObject函数的实现如下:
function CreateComObject(const ClassID: TGUID): IUnknown;
begin
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IUnknown, Result));
end;
之前我们曾讨论过OleCheck,现在将重点的精力放在CoCreateInstance上边。如果根踪CoCreateInstance会发现如下信息:
function CoCreateInstance; external ole32 name 'CoCreateInstance';
因此可以这样认为,CreateComObject函数所作的只是调用了一下Windows的函数CoCreateInstance,并提供了一些默认的参数
CoCreateInstance主要有五个参数:
Clsid:Clsid是我们想要创建的COM服务器的GUID。这是我们专们传递给CreateComObject唯一的一个参数。
UnkOuter:只有在此COM对象是集合的一部分时才使用它。
DwClsContext:dwClsContext决定了读者想要创建的类型。CreateCOMObject自动要求创建一个进程内服务器(CLSCTX_INPROC_SERVER)或本地的进程外服务器(CLSCTX_LOCAL_SERVER)。有时与此函数一起使用的另外一个标记是CLSCTX_REMOTE_SERVER。此标志在DCOM中使用,以后将会讨论。
Iid:iid是我们想要获取一个对它的引用的接口。Delphi通常需要对IunKnown接口的应用,因为如果要其它别的引用的话,大多数类厂会失败。Microsoft用此参数主要是为了将来的扩展。
Pv:主要是得到IunKnown接口的指针。
{一个需要注册的是:CoCreateInstance内部创建负责创建COM对象类厂的实例,然后使用类厂来创建对象。创建完COM对象之后,类厂就被销毁。显然,如果要创建相同COM对象的多个实例,这不是非常有效的,在这种情况下就要自大闯将一个类厂的实例,并在删除它之前使用它的CreateInstance方法来创建COM对象。}
正如以前提到的,CreateComObject通常返回一个IunKnown指针。要获取需要的接口针,应使用as操作符,如:
MyIntf := CreateCOmObject(CLSID_MyServer) as IMyInterface
实例:一个简单的COM应用程序
在讨论了COM服务器的一些基本概念之后,我们用一个简单的小实例来说明如何调用以DLL形式提供服务的COM服务器,之后,我们会对此COM服务器进行扩展,并且会再给一个COM服务器的高级编程,但一切需要一步一步的来。
实例说明:
此实例是当正确登录到COM服务器时,就可以实现一个简单的算法,此处没有给出COM服务器与数据库服务器之间的连接,所以登录不是一个动态的从数据库用户信息表里进行判断,而是在程序中指定了一个固定的用户名,当然,在此处,可以连接一些文本数据库来进行动态的判断。此处的算法很简单,就是传统的小猴子吃桃子的问题,一个小猴子有一些桃子,每天吃一半,因为嘴馋,又多吃一个,这样每天就是吃一半多一个,当到了第十天时,吃完之后仅仅有一个了(此处的吃完仍然是吃了一半多一个),而算法就是要知道小猴子子本来要多少只桃子。
问题分析:因为需要用到COM服务器的一些方法,所以可以暂时作如下的决定:
用户的信息判断,即登录是否成功让服务器来为我们判断,客户端仅仅是作简单的错误性检查和数据的提交,正真的实现一