第八章 对象链接与嵌入(二)
8.3.5.2 在应用程序中释放OLE对象
当一个对象释放到一个窗体,该窗体发生OnDragDrop 事件。该对象定义为TDragDropEvent方法中的Source参数,而TDragDropEvent 方法是用来处理OnDragDrop事件”。 如果Source 是一个OLE 对象, 那么它是TOLEDropNotify 对象的派生类型。 TOLEDropNotify对象有一个与OLE包容器部件PInitInfo属性相对应的PIniInfo属性。 如果一个OLE对象被释放。PInitInfo指向OLE对象的初始化信息结构。要实现释放功能。只需将TOLEDropNotify的PInitInfo属性赋给OLE包容器部件的PInitInfo属性。
以下为处理OnDragDrop事件的代码:
procedure TOLEFrameForm.FormDragDrop(Sender, Source: TObject; X,
Y: Integer);
var
NewChild: TOLEObjectForm;
begin
if Source is TOLEDropNotify then
begin
NewChild := CreateChild;
with Source as TOLEDropNotify do
NewChild.OLEContainer.PInitInfo := PInitInfo
end
end;
注意不要用ReleaseOLEInitInfo释放分配给PInitInfo属性的内存。Delphi自动释放这块内存。
8.3.6 文件中的OLE对象
在OLE应用程序中,要保存对OLE对象的修改,需将对象数据保存在文件中。 如果对象是链接的数据,Delphi将自动的保存在源文件中。当对象被修改时,文件中的数据自动修改。 如果对象是嵌入的,数据贮存在应用程序程序的窗体。要保存对嵌入对象的修改, 应用程序应把数据保存在特殊的OLE文件中。如果要对已存文件的对象进行编辑,应用程序必须从文件中装入OLE对象。
OLE包容器部件的SaveToFile方法可保存对象:
OleCntainer1.SaveToFile('C: \SALEs.OLE');
OLE包容器部件的loadFromFile方法可把文件中的对象装入OLE包容器部件。
OleContainer1.loadFromFile('C:\SALEs.OLE')
本章例程使用了保存对话框和打开对话框来实现运行状态的对象保存和对象装入。
在OLEObjectForm窗体加入保存对话框部件和打开对话框部件。其主要属性如表8.4:
表8.4 保存对话框的属性及取值:
━━━━━━━━━━━━━━━━━━━━━━━━
属性值
────────────────────────
Name SaveAsDialog
DefaultExit ole
FileName .OLE
Filter OLE files (*.OLE)|*.OLE
━━━━━━━━━━━━━━━━━━━━━━━━
表8.5 打开对话框的属性及取值
━━━━━━━━━━━━━━━━━━━━━━━━━
属性取值
────────────────────────
Name OpenDialog
DefaultExit ole
FileName .OLE
Filter OLE files (*.OLE)|*.OLE
━━━━━━━━━━━━━━━━━━━━━━━━━
用户单击“文件|保存”菜单项实现OLE对象的保存。代码如下:
procedure TOLEObjectForm.SaveAs1Click(Sender: TObject);
begin
if SaveAsDialog.Execute then
OLEContainer.SaveToFile(SaveAsDialog.Filename)
end;
用户单击“文件|打开”菜单项实现对象文件装入:
procedure TOLEFrameForm.Open1Click(Sender: TObject);
var
NewChild: TOLEObjectForm;
begin
f OpenDialog.Execute then
begin
NewChild := CreateChild;
NewChild.OLEContainer.LoadFromFile(OpenDialog.FileName)
end
end;
8.4 OLE自动化
OLE自动化是Windows应用程序操纵另一个程序的一种机制。OLE 2.0提供了一种方法来集成应用程序,这就是应用程序之间的命令操作。
利用OLE 2.0,程序员可以定义一组命令,使它们进入到其它程序中。这些命令可带参数。看起来很象应用程序在调用函数或过程一样。采用上述办法, 可以在人不参与的情况下,就能使得两个应用程序的相互作用。
被自动化的程序称作自动化对象或自动化服务器, 操作或自动化其他程序的应用程序称为自动化控制器或自动化客户器。
Delphi2.0完全支持OLE2.0的应用程序自动化,可以用Delphi 2.0编写自动化控制器和服务
器。在应用程序之间可编程的潜能是巨大的。用户可以创建宏或者其它命令, 使得某个应用程序能透过其它应用程序进行工作。已经存在的应用程序的宏语言很容易被扩展,它可以包括一组别的应用程序能够执行的命令和函数调用。
现在介绍两个应用程序,其中MemoEdit.dpr 是多文档界面的文本编辑器,作为OLE自动化服务器,AutoFrom.dpr是自动化控制器。运行AutoForm前,在Delphi集成开发环境中单击菜单(run | parameters),Delphi弹出运行参数对话框,如图8.5,输入参数后运行状态如图8.6。AutoForm窗体的多个按钮。可对MemoEdit进行操作;如按Creat按钮,MemoEdit产生三个子窗体,如图8.7,按"AddText",子窗体将出现"This text was added through OLE Automation"的字符串“
MemoEdit包括三个单元:
Mainfrom MDI主窗体
EditFrom MDE子窗体和自动化类
MemoAuto 应用程序自动化对象
下面结合例程讲述OLE自动化的基本概念及开发。
8.4.1 TAutoObject对象
TAutoObject 是Delphi自动化服务器中所有对象的基类,任何自动化对象都是从TAutoObject类派生出来的。
OLE对象的定义与其它类的定义类似。它的automated部分象普通类的public部分,OLE控制器可引用在这部分声明的属性和方法。编译器把automated部分创建成OLE自动化对象的入口。但automated部分的代码有很多限制:
属性方法可以定义,但不能定义域;
所有属性、参数、函数类型必须是以下类型之一:
SmallInt,Integer,Single,Double,Currency,TDateTime,String,WordBool, Varint
属性声明只能包括访问定义符(read and Write),其它定义符如index,stored,
default,odefault均不能使用;
访问定义符必须列出相应的方法标识符,不能使用域标识符;
支持数组类型;
不允许属性重载;
方法是可以是虚拟的,但不能是动态的,允许方法重载。
在EditFrom单元中定义了TMemoDoc类:
type
TMemoDoc = Class(TAutoObject)
private
FEditForm : TEditForm;
funtion CretFileName : String;
funtion CretModiFied : WordBool;
procedure SetFileName(Const Value : String);
automated
procedure Clear;
procedure Ineart(Const Text : String);
procedure Save;
procedure Close;
procedure FileName : String read GretFileName write
SetFileName;
procedure Modified : WordBool read GretModified
end;
TMemeDoc类是MemoEdit程序的内部自动化类,因此不需要注册。外部OLE自动化控制器对它不能直接引用。如果要使外部控制器对自动化对象进行操作,则要在声明自动化对象的单元中调用Automation. RegisterClass 进行注册。例程MemoAuto 单元定义了TMemoApp对象并进行注册。
unit MemoAuto
…
type
TMemoApp = Class(TAutoObject)
implementation
…
procedure RegisterMemoApp
Const
AutoClassInfo : TAutoClassInfo = (
AutoClass : TMemoApp;
ProgID : MemoEdit,Application
ClassIn : '{FIFF4880 - 200D - 11CF - BDCF - D020AFOE5B81}';
Description : 'Memo Editor Application';
Instancing : acSingle Instance );
begin
Automation,RegisterClass(AutoClassInfo)
end;
inibialization
RegisterMemoApp;
end;
自动化对象要在initialization部分中对自动化对象进行注册。 注册的信息用以唯一辨识服务器对象。把一个自动化对象加入到服务器中要用到这些信息。程序一旦注册了自动化对象,全局自动化对象将用OLE自动化API进行自动管理。
注册后的OLE自动化对象是引用记数的,因为对象可能被多个控制器控制。当使用完一个OLE对象,调用Release方法,Release可减少引用数目,当引用数目为零时,调用Free方法释放对象。
通常把OLE对象作为变体类型(variants)进行输出,任何OLE 对象的方法和属性必须返回一个包含OLE对象的变体类型,TAutoObject提供了一个变体类型的OLEObject属性。控制器不能直接得到服务器中的类或指针,而是引用OLE对象的OLEObject属性。
例程MemoAuto单元的NewMemo函数就是通过引用OLEObject 属性而提供引用TMemoDoc对象的接口。
function TMemoApp,NewMemo : Variant;
begin
Result := MainForm,CreateMemo(' '),OleObject;
end;
8.4.2 创建OLE自动化服务器
OLE自动化服务器是应用程序或动态链接库(DLL),它可向OLE 自动化控制器输出OLE对象。 MemoEditdpr 就是OLE 自动化服务器, 在MemoAuto 单元中注册了MemoEdit.Appdication自动化类,所有OLE控制器均可对MemoEdit.Application进行引用。
在Windows环境下有两种OLE自动化服务器,进程内服务器和进程外服务器, Delphi可创建这两种服务器。
进程内服务器是输出OLE自动化对象的动态链接库。因为OLE自动化对象来自于DLL,
对象是控制器程序的同一窗体进程,进程内服务器适合于创建共享的程序模块, 而这个模块可以被用不同语言编写的多个程序所共享。 进程内服务器被调用时在同一地址中运行,这样就不需要控制器进行调度,以避免处理大量的消息句柄。 进程外服务器是能输出OLE自动化对象的应用程序。
有些OLE自动化服务器只能创建和输出一个OLE对象,有些服务器则可以处理多个OLE对象,另外一些服务器不能输出OLE对象,只能在程序内部使用OLE对象。 服务器与其能输出的对象数目的关系称为实例(instancing)。
在创建OLE 自动化对象时必须定义实例, 这样, 在创建一个OLE 自动化对象时,Windows就能决定是否创建一个新的服务器实例。表8.5列出三种实例类型。
表8.6 实例的取值及含义
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
instancing类型 含义
─────────────────────────────── ───────
internal OLE对象是应用程序的内部对象,对象不需要注册,外部进程不能创
建此对象
Single 每个服务器实例只能输出一个OLE对象实例, 若控制器需要多个OLE
对象实例,WIndows为第一个OLE对象创建一个服务器实例
Multiple 一个服务器能创建和输出多个OLE 对象实例, 进程内服务器大多是
Multiple类型
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
每个使用OLEAuto单元的工程文件自动地拥有一个叫Automation的对象,它是非可视对象。就象Application部件拥有Delphi应用程序的一些信息一样,Automation对象也拥有服务器的一些信息,其中最重要的是StartMode属性和OnLastRelease事件。
StartMode指示OLE自动化服务器打开方式打开的目的。表8.7列出StartMode四种取值。
表8.7 StartMode 的取值及含义
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
取值 含义
───────────────────────────────
SmStandAlone 用户启动应用程序
SmAutomation Windows为创建OLE对象而启动程序
SmRegSever 应用程序仅为注册一个或多个OLE对象而启动
SmUnregSever 应用程序仅为注销一个或多个OLE对象而启动
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
当StartMode模式是SmAutomation,而用户不再需要服务器时发生OnLastRelease 事件。此时所有OLE控制器释放了由服务器创建的对象。缺省情况下,服务器关闭实例,但OnLastRelease 事件可根据实际情况是否关闭。OnLastRelease 事件可得到一个叫ShutDown的布尔型变量。把ShutDown设置成True,则在最后一个OLE对象释放时服务器不关闭。
无论创建何种自动化服务器,必须定义对控制器的界面,包括定义和注册OLE对象,OLE自动化对象的属性和方法。定义界面主要是为了控制器能够引用它们。
对已存在的自动化服务器界进行修改时,要确保向上兼容 ,不要删去已有的属性、方法,这样会导致已存在的自动化控制器发生错误,修改服务器只能增加属性和方法。
创建OLE自动化服务器第一步是创建服务器自身。即创建能输出OLE 对象的应用程序或动态链接库。这主要取决于是创建进程内服务器还是进程外服务器。
创建进程内服务器,即动态链接库:
1.创建动态链接库;
2.在工程文件的uses条款中加入OLEAuto单元;
3.在DLL中输出四个标准入口,即加入以下代码。
exports
DLLGetClassObject,DLLCanUnloadNow;
DLLRegisterServer,DLLUnregisterServer;
以上代码必须准确拼写,包括大小写。与Object Pascal的其它项目不同,这些代码
对大小写敏感。
创建进程外服务器:
1.创建一个Delphi应用程序;
2.在工程文件的begin之后加入以下代码;
if Automation,Server Registration then Exit;
创建服务器之后,应该向服务器加入OLE自动化对象,这个过程大部分是自动完成的,但必须向Delphi的自动化对象专家提供必要的信息。
把OLE自动化对象加入服务器:
1.在Delphi集成开发环境中选择File| New 菜单项, 并在对象集中选择Automation
Object,Delphi打开自动化对象专家。
2.给自动化对象命名
这是服务器内部标识OLE对象的名字,必须是个有效的面象对象Pascal标识符,习惯上以T字母开头;
3.给OLE类命名
该名用以外部控制器创建对象。当服务器在Windows中注册OLE对象, 就以这个名字在系统注册。控制器使用这个名字调用CreateOLEObject来创建对象。
4.描述要输出的对象。
5.定义对象的实例(instancing),进程内服务器常定义为Multiple,进程外服务器常定义为Single;
6.选择OK键完成该过程
自动化对象专家将产生以下代码:
从TAutoObject派生下来的自动化对象定义,但没有定义任何属性方法;
调用DelphiOLE自动化管理器的注册代码,管理器负责Windows中注册服务器和对象。
在注册代码中包括一个自动产生的ID号,这个ID号是全局唯一的,通常不要修改。每个ID号与一个OLE类名相对应,如果其中之一被改变,应用程序在使用时会发生错误。
在创建了服务器并把OLE自动化对象加入服务器之后,控制器程序就可以对服务器进行操纵。
8.4.3 自动化另一程序
每个服务器在系统注册中有一个叫ProgID的关键定,主要用以控制器辨识服务器。任何控制器可以用ProgID号来创建OLE对象实例。例程AutoForm是控制器程序,它在其主窗体创建了OLE对象实例。
procedure TMainForm.FormCreate(Sender : TObject);
begin
try
MemoEdit := CreateOleObject('MemoEdit.Application');
except
MessageDlg(
'An instance of the "MemoEdit Application"OLE Automation Class could
not be created,Make sure that the MemoEdit application has been registered
using a "MemoEdit|regserver"command line',
mtError,[mbok],0)
Halt;
end;
end;
控制器创建了OLE自动化对象实例后,可对其进行操纵。OLE自动对象包括属性和方法,虽然OLE自动化对象与面向对象Pascal中的对象不是同一概念,但Delphi允许使用与类似的语法对OLE对象的方法进行调用。
AutoForm的很多过程引用了OLE自动化对象的方法:
procedure TMainForm,TileButtonClick(Sender : Tobject);
begin
MemoEdit,TileWindow;
end;
其中TileWindows是OLE对象TMemoApp中定义的方法。
AutoForm还通过TMemoApp的NewMemo方法获得了对服务器内部OLE对象TMemoDoc 的引用。
procedure TMainForm,CreateButtonClick(Sender : TObject);
var
I : Integer;
begin
CloseMemo
for I := 1 to 3 do Memos[2] := MemoEdit.NewMemo;
end;
其中NewMemo在MemoAuto单元中定义如下:
function IMemoApp.NewMemo : Variant;
begin
Result := MainForm,CreateMemo(' '),OleObject;
end;
控制器在获得服务器的内部OLE对象后,可以引用其方法:
procedure TMainForm.AddTextButtonClick(Sender,TObject);
var
I : Integer;
begin
for I := 1 to 3 do
if not var IsEmpty(Memo[I]) then
Memo[I],Insert{'This text was added through OLE Automation'#13#10);
end;
Insert是TMemoDoc中定义的方法,用以在子窗体中插入字符串。