TYPE
TOLEForaneForm = Class(TForm)
…
private
FEmbedClipFmt: Word;
FLinkClipFmt: Word;
function CreateChild: TOLEObjectForm;
public
Fmts: array[0..1] of BOleFormat;
end;
在注冊剪貼板格式後, 還必須定義OLE 格式才能進行對象粘貼。 每種格式定義在BOLEFormat記錄中。 程序中可能注冊標准剪貼板格式並用這種格式進行粘貼。例如:注冊文本作為粘貼格式,將BOLEFormat記錄為fmtId域定義為CF_TEXT,fmt Medium 域定義為BOLE_MED_HGLOBOL。 BOLEMediumCalc 函數可以根據定義的剪貼板格式計算出fmtMedium值。在本章例程中,程序注冊了兩種格式,一種是鏈接OLE對象的格式,另一種是嵌入OLE對象的格式。
BOLEFormat類型定義在BOLEDefs單元中,BOLEMediumCalc函數定義在ToCtrl單元。因此主窗中的interface部分應加入這兩個單元。
interface
use…,BOLEDefs,ToCtrl,
在粘貼OLE對象前,應用程序必須知道在剪貼板中是否有OLE對象。
PasteSpecialEnabled函數可判斷粘貼對話框是否有效。如果剪貼板上有Fmts定義的任何一種格式,PasteSpecialEnable將返回真值, 粘貼對話框才能成功地調用。反之調用粘貼對話框將不發生任何事件。
以下代碼實現“編輯|粘貼”菜單項的功能:
procedure TOLEObjectForm.PasteSpecial1Click(Sender: TObject);
var
ClipFmt: Word;
DataHand: THandle;
Info: Pointer;
begin
if PasteSpecialEnabled(Self, OLEFrameForm.Fmts) then
if PasteSpecialDlg(Self, OLEFrameForm.Fmts, 0,
ClipFmt, DataHand, Info) then
InitializeOLEObject(Info)
end;
只有在粘貼對話框有效時“編輯|粘貼”菜單才有效,以下代碼實現此功能:
procedure TOLEObjectForm.Edit1Click(Sender: TObject);
begin
PasteSpecial1.Enabled := PasteSpecialEnabled(Self, OLEFrameForm.Fmts)
end;
8.3.5 釋放OLE對象
從OLE服務器拖動OLE對象並將其放在OLE應用程序是一種方便的對象鏈接與嵌入的方法。通過拖放操作,用戶不需要使用插入對話框或粘貼對話框來定義OLE對象。而只需用鼠標鍵從OLE服務器中“抓”住OLE對象,拖至OLE應用程序,松開鼠標鍵,從而實現OLE對象的插入。
8.3.5.1 注冊OLE釋放目標窗體
為了接收一個釋放的OLE對象,必須有一個窗體在Windows中注冊成OLE釋放目標,用RegisterFormASOLEDropTarget函數可實現此功能。
RegisterFormASOLEDropTarger(Form : TFrom;Const Fmts: array of BOlefrom).
其中Form是OLE對象的釋放目標窗體,在本章例程中,將子窗體傳遞給Form參數。
Fmts是對象格式的數組。它是BOLEFormat 類型的數組。 所有要釋放的數據必須用Fmts數組中相應BOLEFormat元素注冊。
在本章例程中,注冊的Fmts 數組與主窗體OnCreate事件 聲明的數組相同, 即:聯接對象格式和嵌入對象格式。如果想接收更多類型的釋放數據,就必須在Fmts數組中加入其它元素。例如應用程序要接收釋放的文本,Fmts需加第三個元素, 其fmtId 域為CF_TEXT,BOLEMedium域為BOLE_MED_HGLOBL.
拖放過程中不需要用BOLEFormat的fmtName,fmtResultName域,如果程序只進行拖放操作而不進行對象粘貼,可以不初始化兩個域。
在主窗體的OnCreate事件中可調用RegisterFormAsOLEDropTorget。
procedure TOLEFrameForm,FormCreate(Sender : TObject);
begin…
Register FormASOleDropTarget(Self,Fmts)
end;
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中定義的方法,用以在子窗體中插入字符串。