DELPHI基础教程
第八章 对象链接与嵌入(二) 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 中定义的方法,用以在子窗体中插入字符串。 |