2023年11月30日发(作者:)

Delphi 中的COM 编程

1 接口

1.1 定义接口:

目的:什么是接口,以及和抽象类的关联以及不同点。

抽象类(备注理解接口最简单的方法)

永远不能创建一个抽象类的实例;

本身不能实现功能,依靠派生类实现;

接口

被申明为interface类型。接口名从字母I开始。类类型名从T开始。

所有的接口从IUnknown继承;

不能创建接口实例;

不能在接口中指定范围指示。所有的方法都是公有型(public,不能在接口中申明包括

范围指示;

不能申明变量;接口只能决定提供什么样的功能,对于如何完成功能没有限制。

接口中申明的所有函数和过程,概念上讲都是虚(virtual)抽象函数和过程。申明时不

能带virtual;

接口是不变的;

1.2申明一个接口

目的:如何声明一个接口

GUID(Globally Unique Identifier)全球唯一标示符:CoCreateGuid产生(API)

1.3 实现接口

目的:如何实现接口

实现IUnknown QueryInterface _AddRef _Release

使用TInterfaceObject来自动实现Iunknown,否则的话自己要实现上面的方法。

创建、使用及销毁接口: create;指向接口的指针不访问任何信息;自动释放、强迫销

毁一个接口将变量置为nil

注:delphi自动创建和销毁接口。

获取单个接口的指针:

直接分配:类与他们实现的接口类型兼容的

var MyInteger: TClass;

myNumber: Iinterface;

begin

myInteger:= ;

myNumber:=myInteger;

end;

GetInterface(const IID: TGUID; out obj):判断对象是否支持一个接口

Var MyObject : TObject;

myInterface : Iinterface;

begin

myObject := ;

if erface(Iinterface,myInterface) then

end;

as操作符: 对象支持特定的接口(对象不支持接口就错的话,可以拦截错误)

要使用getinterface as 的话必须要有一个guid

as自动调用计数功能;

1.4 高级多级接口问题

目的:在一个类中实现多个接口

在一个类中实现多个接口

TXY = class(TInterfacedObject, IXX, IYY) TXY 实现了IXXIYY接口的所有方

法。

多个接口不是多重继承:TXY有且只有一个基类TInterfacedObject;

方法分辨字句:当接口方法在类中实现时,方法分辨子句可使用改变他的名称

TXY = class(TInterfacedObject, IXX, IYY)

procedure = pxy1

procedure IYY.pxy = pxy2

接口授权:一个接口的实现授权给另一个类:一个类包含针对另一个类的指针。

内部类: 实现一个或多个接口的功能性;

外部类: 简单的将这些方法传递给内部类,而不是重新实现接口;

接口属性:可以定义只读、只写、或者读写属性;

但是所有访问都必须通过访问函数,因为接口不能定义存储。

1.5 小结

目的:如何在delphi应用程序中内部使用接口,了解delphi语言要素的接口。

申明一个接口;

在类中实现接口;

实现IUnknown所需要的功能;

自动对象析构的处理;

在类中实现多个接口;

将一个接口的实现授权给一个内部对象;

定义并实现接口属性

2 COM/DCOM

COM (Component Object Model )

COM/DCOM可以说是windows平台最基本和重要的技术,现在几乎所有microsoft的软

件都是以COM/DCOM技术开发而成的。COM/DCOM基本上是一种组件模型,它提供

了一些基本的原则方法让程序员在windows平台中使用标准的对象模型来开发软件组

件,由于这些软件组件遵循了microsoft定义的标准,因此这些组件可以使用在任何支持

COM/DCOM标准的软件中,让这些软件组件提供的服务可以被不同的软件重复的使用。

COM/DCOM软件借助接口来提供它的服务,一个COM/DCOM对象可以同时提供数个

不同的接口,并且在不同的接口中提供不同的方法和属性。

COM/DCOM至少提供并且实现一个称为IUnknown的接口,这个接口提供了最基本且

必要的方法,它是所有其它接口的父借口,其它借口都是从他继承而来的。

COM/DCOM对象和他所有的接口都必须有所谓的CoClass(Component Object Class)来加

以定义。如果不定义在一个CoClass中就说明COM/DCOM不支持这个接口,客户端也

无法取得这个接口提供的服务。

CoClass是类别的定义,在使用COM/DCOM对象前,必须先使用一种方法从类别定义

建立真正的类别对象,接着再从类别对象中取出需要的接口,最后从取得的接口中调用

需要执行的方法或需要存取的属性

COM/DCOM中接口代表的是一群服务的定义,当定义好接口后还必须用Object Pascal

编写实现接口的实际程序代码。

COM/DCOM两种接口类型:原生COM格式的接口和Automation接口。

CLSID代表一个COM/DCOM对象(CoClass)的类别

IID 代表一个接口

APPID 代表一个应用程序的ID

PROGID

建立COM/DCOM对象

应用程序在调用COM对象提供的服务前,必须在内存中先建立COM对象的实体样例,

然后才能从COM实体样例中取得接口,再根据接口调用其中的方法。

COM执行时期函数库APICoCreateInatance, CoCreateInstanceEx前者较旧用于建立

COM后者用于建立DCOM对象。

Delphi中可以用CreateComObjectCreateRemoteComObject两个函数,分别封装了前

面两个API

客户端应用程序

1. CoCreateInstance

CoCreateInstanceEx

动态连接库

5. 应用程序借助接口调用

COM对象

2.加载DLLEXE文件

内存

SCM (Service Control Manager)

3. 根据ClassID查找DLL

EXE文件

4. 加载DLLEXE到内存

注册表

DLL文件 EXE文件

取得COMDCOM接口

可以用CreateComObject直接创建一个需要的接口,也可以先创建一个IUknown的接口

然后转化,IUnknown中有一个QueryInterfase的方法可以让程序员取得需要的其它接

口,delphi中自动提供了这个方法,只要使用as操作符就能自动从一个接口取得另外一

个接口。

执行程序

COM服务器分为进程内服务器(In-Process Server)和进程外服务器 (Out-Process

Server)

In-process Server

位于dll文件中,它的调用非常快速,就像其他程序调用一样。

Out-Process Server

位于 exe文件中,调用它时会自动执行一个执行程序,使用进程外服务器时要穿越不同

的执行程序,所以他的速度比进程内的服务器快许多。

客户端执行程序

操作系统

SCM

载入

DLL文件

载入

客户端执行程序

操作系统

服务端执行程序

Proxy Stub

由于在调用COMDCOM组件时可能会穿越不同的执行程序,也可能会穿越不同的机器,

因此在许多情况下应用程序无法直接调用组件,而必须通过ProxStub的帮助。

简单的说,Prox就是远程COM对象在应用程序的执行程序中的影像,远程COM提供

了什么接口和方法,它的Prox就有什么接口和方法。Prox被调用后就会封装这些信息

通过通信协议调用远程的Stub

Stub接受Prox的调用信息,在用这些信息调用真正的COMDCOM对象,在把结果返

回给ProxProx再回传给应用程序。

Prox/Stub来源,如果是原生的COM对象,他们是有微软的midl编译器根据COMDCOM

IDL文档编译出来的。如果是Automation接口,它是windows自带的。

远程对象调用

当应用程序调用远程机器上的DCOM组件时,除了必须通过Prox/Stub之外,还要一个

实体的通信协议来传递调用信息,这个实体通信协议便是RPC(Remote Procedure Call)

RPCNT的内定服务,一般自动启动。

SCM

启动

机器一

机器二

Prox Stub

DLL DLL

自动化

COM\DCOM线程模型

Apartment 就是把执行程序的空间细分,以维护线程同步存取的问题。在一个执行程

序中可以拥有数个不同的Apartment每个Apartment可以使用不同的线程模型来控制线

程如何存取组件。

Single-Thread Apartment(STA)

一次只能有一个线程进入此Apartment中存取属于这个Apartment的组件。这样程序员

就不用考虑存取同步问题。在一个执行程序中可以同时有许多的STA。

Multi-Thread Apartment

允许有几个先程进入这种Apartment中存取其中的对象。应次程序员需要控制同步问题。

Both Apartment

两种都可以,也要控制同步问题。

DLL Surrogate

Surrogate负责加载实现COMDCOM对象的DLL文件,让这个DLL执行在Surrogate

的执行程序空间中。

MTS是一个,NT自带也有一个.

自动化是一种从应用程序的内部自动控制另一个应用程序的方法。

获取自动化服务器的COM对象的两种主要访问方法:

接口:早期连接(early binding)

早期连接是指对接口方法的所有调用在编译时检查参数是否正确;

Variants: 后期连接 (late binding)

后期连接指连接意味着方法调用直到运行时才被实现;

Variant不是对象指针;对象的调用方法是后期连接;

派遣接口(DispInterface)

在接口和Variant中间的某个地方就是派遣接口;与接口有很多类似;

只是方便客户而设定的;

并没有在服务器上实现派遣接口;是服务器上实现了接口;

RPC

假设服务器的COM对象也支持IDispatch接口,客户应用程序可以使用Variants或派遣

接口;

双重接口(Dual Interface) 自动化服务器

简单定义为自动化服务器,支持早期连接(接口)和后期连接(Variants)

使用delphi创建的任何任何自动化服务器将自动支持双重接口;

进程内自动化服务器

CreateOleObjectGetActiveOleObject

CreateOleObject:总是创建特定服务器的新实例;

GetActiveOleObject: 用来获取正在内存中运行的服务器的引用;

如:Procedure StartOrLinkToWord;

var v: Variant;

begin

try

v := GetActiveOleObject(''); //判断是否Wod已启动;

except

v := CreateOleObject(''); //否则启动Word;通常自动化服务器启动是隐藏的;

w; //显示Word应用程序;

end;

V.FileNew; //创建一个新的文档;

('Automation is easy!');

end;

进程外自动化服务器

目的: 自动化的基础;创建一个Automation服务器,如何使用接口、派遣接口、Variants

来控制此服务器;

在本身应作为独立服务器应用程序的情况下,使用进程外非常好;

HResult:

自动服务器中的所有方法必须返回一个HResult,暗示是成功还是失败;

所有其他参数必须在out参数中返回;

safecall

调用协议可使编码;

指示Delphi自动把所有方法包括在pt模块中;

在客户端safecall导致客户检查是否有HResult类型的返回失败码;