7.2.2 Delphi的DDE實現機制簡介
Delphi把所有的DDE功能做到四個部件中,它們是:
● TDDEClIEntConv : 用於客戶程序建立和維護一個DDE會話
● TDDEClIEntItem : 用於客戶程序建立和維護數據交換通道
● TDDEServerConv : 用於服務器程序響應DDE會話
● TDDEServerItem : 用於服務器程序維護數據交換通道
前兩個部件用於生成一個DDE客戶程序,後兩個部件用於生成一個DDE服務器程序。如果一個應用程序同時擁有這些部件,則這一程序既可以充當DDE客戶,也可以充當DDE服務器。
會話部件TDDEClIEntConv、TDDEServerConv用於建立和維護一個DDE會話。DDE會話包括DDE服務和DDE主題兩部分。
DDE服務是DDE服務器的名稱,即在一般的Windows DDE機制中所講的應用程序名。一般說來這一名稱是DDE服務器應用程序執行文件名去掉 .EXE後綴。比如你的應用程序要和Word 6.0建立會話,則DDE服務為WINWord。但也不盡然。比如你的應用程序要和Borland ReportSmith ( RPTSMITH.EXE ) 建立會話,則DDE 服務為 Report Smith。DDE服務到底如何,讀者可參看相關的DDE服務器應用程序文檔。
DDE主題是一個包含了聯接信息的數據單元。一般說來DDE 主題是一個包括擴展名的完整文件名。例如和Excel中的一個文件建立DDE會話,則主題可能是
Topic = 'c:\Excel\Example\sale.xls'
如果服務器是一個Delphi應用程序,缺省情況下主題是包含欲聯接數據窗體的標題。如果服務器使用了DDEServerConv部件,則要求使用部件DDEServerConv的名稱作為DDE主題。
項目部件TDDEclIEntItem、TDDEServerItem用於建立和維護DDE數據的傳輸通道。 DDE項目中包含著實際欲傳輸的數據。DDE項目的格式取決於DDE服務器應用程序。一個可能的DDE項目例子是電子表格中的單元和數據庫表中的域。如果服務器是Delphi應用程序,則項目是連接的 DDEServerItem部件的名稱。
Delphi的DDE實現機制方便、實用,但也有一個令人遺憾的缺陷:只能傳輸文本數據以及命令、宏,而不能傳輸圖像數據。在這一點上微軟公司推出的Visual Basic 要略勝一籌。不過在目前文本數據的使用仍是最廣泛的,而且圖像傳輸可以利用剪貼板和OLE來實現,則這一缺陷也並無很大的影響
7.3 DDE客戶程序的實現
DDE客戶程序啟動DDE會話,向服務器請求並從服務器接收數據。同時還可以向服務器發送數據、命令、宏,改變服務器的狀態並控制服務器的運行。
7.3.1 聯接模式(ConnectMode)
Delphi的DDE提供了兩種聯接模式:自動和人工。這可以通過DDEClinetConv 部件的ConnectMode屬性進行設置。如下表所示。
表 7.5 DDE的聯接模式
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
值 意 義
───────────────────────────────
ddeAutomatic 在運行中當包含TDDEClIEntConv部件的窗口創建時
聯接自動建立
ddeManual 只有當調用OpenLink方法時聯接才建立
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
不同聯接模式,DDE客戶程序的實現方式不同。
對於自動模式:
1. 向窗體中加入DDEClientConv和DDEClIEntItem部件並命名;
2. 把DDEClientItem部件的DdeConv屬性設置為DDEClIEntConv部件的名稱;
如果在設計時建立,則通過對象觀察器進行選擇;如果在運行時建立聯系, 則通過如下的一條語句設置屬性的值:
DDEClientItem1.DdeConv := 'DDEClIEntConv1' ;
3. 和服務器建立聯系,實現數據共享。
對於人工模式:
1.向窗體中加入DDEClIEntConv部件;
2.和服務器建立聯系;
3. 數據更新時調用RequestData方法申請並獲得數據。
7.3.2 和DDE服務器建立聯系
和DDE服務器建立聯系,既可以在設計時進行,也可以在運行時進行。
在設計時,DDE聯接可以通過剪貼板進行粘貼。具體步驟如下:
1. 激活服務器程序,並選中你的客戶程序欲聯接的數據;
2. 把數據和DDE聯接信息拷貝到剪貼板上。一般說來這只需要選擇服務器應用程序的 Edit|Copy 菜單;
3. 在Delphi IDE的設計窗體中選中DDEClIEntConv部件;
4. 在Object Inspector(對象觀察器)中單擊DDEService屬性或DDETopic屬性,然後再單擊Ellipsis按鈕,打開DDE Info對話框;
5.選擇Paste Link按鈕。此時App編輯框和Topic編輯框被自動填充。如果Paste Link按鈕變灰,說明你准備用作服務器的應用程序不支持DDE或者DDE信息沒有被成功地拷貝到剪貼板上;
6.選擇OK 按鈕。此時Object Inspector中的DDEService、DDETopic 屬性包含了建立一個DDE聯接的正確值。
對於人工模式以下步驟是不需要的。
7.選中DDEClientItem部件,並在Object Inspector中設置DdeConv屬性為已完成聯接的DDEClIEntConv部件名稱;
8.假如剪貼板上的DDE 聯接信息仍保留的話,從Object Inspector的下拉列表框中選擇 DDEItem 屬性的值。否則輸入正確的值。
在運行時,調用 SetLink 方法來建立DDE聯接。
SetLink有兩個String類型的參數,分別用來接受DDEService和DDETopic的值。過程執行後DDEClIEntConv部件的DDEService 和DDeTopic屬性被設置。要注意的是:在運行時直接設置DDEService和DDETopic的值並不能建立一個DDE聯接,而必須調用SetLink 方法進行初始化。
比如,下面的語句和Excel的System主題建立聯接:
DDEClIEtnConv. SetLink('Excel','System');
調用SetLink方法後,還需要設置DDEClIEntItem部件的DDEItem屬性。
比如,下面的語句聯接Excel的Topics項目,用以獲取當前活躍文件的文件名:
DDEClIEtnItem.DDEItem := 'Topics';
當DDE聯接建立後,聯接的數據保存在DDEClIEntItem部件的Text和Lines 屬性中,Text用於保存一個字符串(String),Lines用於保存一個字符串鏈表(TStrings)對象。
為了顯示聯接數據,可以在DDEClIEntItem的OnChange事件中把數值賦給一個可視部件。
下面的事件過程把聯接數據實時地顯示在一個編輯框中。
procedure Form1.DDEClIEntItemChange(Sender: Tobject);
begin
Edit1.Text := DDEClIEntItem1.Text;
end;
運行狀態下也可以從剪貼板上粘貼DDE聯接信息,並調用SetLink建立DDE會話。下面的例子顯示了當用戶按下應用程序中的Paste Link按鈕時,動態建立DDE會話的過程。
procedure Form1.OnPasteLink(Sender: Tobject);
var
Service, Topic, Item: String;
begin
if GetPasteLinkInfo (Service, Topic, Item) then
begin
AppName.Text := Service;
TopicName.Text := Topic;
ItemName.Text := Item;
DDEClIEnt.SetLink (Service, Topic);
DDEClientItem.DdeConv := DDEClIEnt;
DDEClIEntItem.DDEItem := ItemName.Text;
end;
end;
GetPasteLinkInfo是DDEMan 庫單元中定義的一個過程。如果返回True,則DDE聯接信息保存在三個參數中;如果返回False,說明剪貼板上沒有正確格式的DDE聯接信息。
7.3.3 數據申請
雖然自動模式快捷、方便,但仍有一些理由使用DDE的人工模式:
1.服務器程序可能不支持自動數據傳輸,客戶必須顯式申請服務器更新一個特定的項目;
2.節省通信費用。假如沒有實時傳輸的要求,則人工模式可以大幅度降低通信的開銷;
3.若客戶程序只用於控制服務器的運行,則往往沒有必要使用自動模式。
人工模式下客戶程序的數據更新需要采用數據申請的方式。數據申請需要調用DDEClIEntConv部件的RequestData方法。RequestData有一個參數,指向要申請的DDE項目。RequestData返回一個Pchar類型的無結束符字符串,包含了申請到的文本。返回字符串占用的內存必須在程序終止前顯式釋放。
在人工模式下,即使存在一個DDEClientItem部件且與DDEClientConv相聯接,數據更新後DDEClIEntItem部件的Text、Lines屬性的值也不會改變。
7.3.4 數據發送
數據發送與一般的DDE數據流向正好相反,是把數據從DDE客戶應用程序發送到DDE服務器應用程序。
數據發送使用DDEClIEntConv部件的兩個方法PokeData 和 PokeDataLines, 它們的語法是:
function PokeData (Item: String ; Data: PChar): Boolean;
function PokeDataLines (Item: String ; Data: TStrings): Boolean;
參數Item是DDE服務器中被聯接的項目,Data是要發送的數據。如果數據是一個字符串,則把它轉化為PChar類型並調用PokeData方法;如果數據是一個字符串鏈表對象,可調用PokeDataLines方法。
方法的返回值標志數據傳送是否成功。因為有一些DDE服務器應用程序並不接收發送的數據。
下面的語句把編輯框中的內容發送給服務器:
StrPCopy(TheText , Edit1.text);
DDEClientConv1.PokeData(DDEClIEntItem1.DDEItem , TheText);
過程StrPCopy把一個Pascal類型的字符串拷貝到一個無結束符的PChar類型字符串中。
7.3.5 控制服務器應用程序的執行
客戶程序控制服務器應用程序的一個方面是:必要的時候客戶程序可以啟動服務器程序,並裝載會話主題。
而客戶程序控制服務器應用程序更重要的一點是向服務器發送服務器承認的宏命令,來完成對服務器應用程序的各種操作。服務器到底支持哪些宏命令,可參閱服務器應用程序文檔。
發送宏命令要使用DDEClIEntConv的兩個方法 ExecuteMacro和ExecuteMacroLines ,它們的語法如下:
function ExecuteMacro(Cmd: PChar; WaitFlag: Boolean): Boolean;
function ExecuteMacroLines(Cmd: TStrings;WaitFlag: Boolean): Boolean;
Cmd是欲發送的宏命令字符串或宏命令字符串鏈表。WaitFlag決定了在DDE 服務器程序執行宏命令時客戶程序的行為。如果WaitFlag設置為True,則在服務器宏命令執行完畢前,不允許對ExecuteMacro、ExecuteMacroLines、PokeData、PokeDataLines這些方法的成功調用,它們都不向服務器發送數據並返回False。如果WaitFlag設置為False,則調用的方法在第一個宏執行完畢前即試圖向服務器發送數據。
WaitFalg的設置也取決於服務器應用程序。一些應用程序當在第一個宏執行完之前就試圖向它發送數據或命令時,可能導致第一個宏執行失敗或導致不可預料的後果。具體情況可查閱服務器應用程序文檔。
函數返回值表示命令串是否被成功傳輸。而宏命令執行是否成功客戶是無法檢測到的。
7.3.6 格式化文本
DDEClIEntConv有一個布爾屬性FormartChars,用於決定是否格式化文本。所謂格式化文本是指從傳輸來的文本數據中過濾掉BackSpace(8)、 Tab(7) 、Linefeed(10) 、Return(13)等字符。括號內是字符的ASCII碼。許多時候這些字符將導致DDE客戶數據顯示的混亂。
FormatChars的缺省值是False。
7.3.7 響應DDE事件
部件DDEClientConv有兩個事件OnOpen和OnClose,分別在DDE 會話建立和中止時觸發。部件DDEClIEntItem有一個OnChange事件。這一事件常用於DDE項目數據的轉儲和顯示,如(7.3.1)節所示。
在自動模式下,OnOpen事件在包含DDEClIEntConv部件的窗口創建時觸發,或在調用SetLink方法時觸發,OnClose事件在客戶程序或服務器程序關閉時觸發。
在人工模式下,OnOpen事件在調用OpenLink 方法時觸發,OnClose事件在調用ColseLink方法時觸發。
7.3.8 利用客戶程序和Excel交換數據
下面我們建立一個DDE客戶程序,並利用這一程序與Excel中的一個工作表交換數據。程序設計界面
界面中包含一個DDE會話部件DDEClientConv1和DDE項目部件DDEClIEntItem1,用於建立和維護DDE聯接;一個RadioGroup控件和其中的兩個無線電按鈕AutoRadio、ManualRadio,用於設置聯接模式;一個GroupBox控件和其中的兩個按鈕RequestBtn和PokeBtn,用於控制數據的申請和發送,其中RequestBtn在自動模式下變灰;一個文本框Memo1用於保存DDE數據;一個按鈕PasteBtn用於粘貼聯接信息並建立DDE聯接;另外一個按鈕CloseBtn用於關閉系統。
設計時把DDEClIEntConv1的FormatChars屬性置為True,這樣可以保留服務器傳來數據的顯示格式;ConnectMode保留ddeAutomatic的缺省設置。
程序在類TForm1中定義了一個私有數據成員Automatic,用於標志聯接模式;三個字符串數據成員DDEService、DDETopic、DDEItem用於記錄聯接信息。
窗口生成時進行變量和部件狀態的初始化。
procedure TForm1.FormCreate(Sender: TObject);
begin
RequestBtn.Enabled := False;
AutoRadio.Checked := True;
Automatic := True;
end;
當聯接模式改變時,程序進行相應的處理。
自動模式轉換為人工模式:
procedure TForm1.ManualRadioClick(Sender: TObject);
begin
if Automatic then
begin
RequestBtn.Enabled := ManualRadio.Checked;
DDEClIEntConv1.ConnectMode := ddeManual;
Automatic := False;
end;
end;
人工模式轉換為自動模式:
procedure TForm1.AutoRadioClick(Sender: TObject);
begin
if not Automatic then
begin
RequestBtn.Enabled := ManualRadio.Checked;
If (DDEService = '') or (DDETopic = '') then
begin
MessageDlg(' Can not Set Link.',mtWarning,[mbOK],0);
Exit;
end;
DDEClIEntConv1.SetLink (DDEService, DDETopic);
DDEClientItem1.DdeConv := DDEClIEntConv1;
DDEClIEntItem1.DDEItem := DDEItem;
DDEClIEntConv1.ConnectMode := ddeAutomatic;
Automatic := True;
end;
end;
當從自動模式轉換到人工模式,只需要簡單修改相應屬性即可;而從人工模式轉換到自動模式,則需要調用SetLink重新建立聯接,否則往往會引發一個DDE異常。
聯接的建立采用從剪貼板粘貼聯接信息的方式,這是最具有靈活性的一種方法。
procedure TForm1.PasteBtnClick(Sender: TObject);
begin
if GetPasteLinkInfo (DDEService, DDETopic, DDEItem) then
begin
DDEClIEntConv1.SetLink (DDEService, DDETopic);
if Automatic then
begin
DDEClientItem1.DdeConv := DDEClIEntConv1;
DDEClIEntItem1.DDEItem := DDEItem;
end;
end;
end;