後一頁 前一頁 回目錄 回首頁 |
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; GetPasteInfo是 DDEMan庫單元中定義的一個函數,用於偵測剪貼簿上是否有聯接資訊並返回相應的DDE服務、主題和項目。 對於人工模式,必須由用戶顯式向伺服器申請數據。在這種模式下DDE項目部件是多餘的,接收到的DDE聯接資訊用一個字元串來記錄。下面是實現代碼。 procedure TForm1.RequestBtnClick(Sender: TObject); var TheData: PChar; begin If DDEItem = '' then begin MessageDlg('Can not Request Data',mtWarning,[mbOK],0); Exit; end; TheData := StrAlloc(79); DDEClientConv1.OpenLink; TheData := DDEClientConv1.RequestData(DDEItem); DDEClientConv1.CloseLink; if TheData <> nil then Memo1.Text := StrPas(TheData); StrDisPose(TheData); end; OpenLink、CloseLink方法用於打開和關閉聯接。RequestData方法向伺服器申請數據並返回到一個PChar字元串中。字元串必須顯式分配記憶體並在結束時釋放。 數據發送在不同聯接模式下是不同的。對於人工模式,增加了聯接的打開和關閉操作。程式清單如下。 procedure TForm1.PokeBtnClick(Sender: TObject); begin If DDEItem = '' then begin MessageDlg('Can not Poke Data.',mtWarning,[mbOK],0); Exit; end; if Automatic then DDEClientConv1.PokeDataLines(DDEItem,Memo1.Lines) else begin DDEClientConv1.OpenLink; DDEClientConv1.PokeDataLines(DDEItem,Memo1.Lines); DDEClientConv1.CloseLink; end; end; 打開Microsoft Office中的Excel,載入一個文件,把相關的單元選中,拷貝到剪貼簿上。而後執行程式,按下Paste Link按鈕,DDE聯接就建立起來,相關單元中的數據顯示在Memo1中。之後可以進行模式轉換、數據申請、申請發送等一系列工作。執行後的螢幕顯示如下圖所示。 7.3.9 用用戶程式控制過程管理器 下面的例子用用戶程式向過程管理器發送命令,用於建立程式群群組、程式項目以及解除程式群群組。 過程管理器提供了應用程式的DDE接口命令字元串,應用程式利用這些命令字元串可以實現以下的功能: 1.建立程式群群組 命令格式為: CreateGroup(程式群群組名[,程式群群組所在的路徑]) 程式群群組不存在時進行建立;如程式群群組存在則按照指定的路徑激活。 2.解除程式群群組 命令格式為: DeleteGroup(程式群群組名) 3.顯示程式群群組 命令格式為; ShowGroup(程式群群組名,顯示標誌) 顯示標誌用於控制程式群群組在過程管理器中以極大、極小或正常方式顯示。 4.重新載入程式群群組 命令格式為: ReLoadGroup(程式群群組名) 該命令使過程管理器先解除而後再重新載入一個已有的程式群群組。 5.向程式群群組中加入程式項目 命令格式為: AddItem(命令行[,描述[,圖示路徑[,圖示序號[,圖示橫坐標,圖示縱坐標[,工作區目錄[,熱鍵[,是否最小化顯示標誌]]]]]]]) 命令行控制程式項目的執行,可包括路徑、參數等。其它參數分別對應在過程管理器中加入一個程式項目時需要設定的參數和選項。它們都有缺省設定,因而是可選的。 6.覆寫程式群群組中的程式項目 命令格式為: ReplaceItem(程式項目名) 該命令解除一個程式項目,並將所解除程式項目的位置記錄下來,以後通過AddItem在這個所記錄的位置增加新項目。 7.從程式群群組中解除程式項目 命令格式為: DeleteItem(程式項目名) 從目前活動程式群群組中解除一個程式項目。 8.關閉過程管理器 命令格式為: ExitProgram(是否存檔程式群群組資訊標誌) 從應用程式向過程管理器發送命令字元串的方法是基本一致的。為簡便起見,在例程中只實現了其中僅包含一個字元串參數的情形,讀者可以很容易作進一步的擴展。 程式設計介面如圖所示,包含一個DDE用戶交談(DDEClientConv)部件和四個完成不同功能的按鈕。 DDEClientConv在設計時和過程管理器建立一個DDE交談,其中DDE伺服器和DDE主題 都為PROGMAN。聯接模式ConnectMode設定為ddeManual。 我們把只有一個字元串參數的命令發送情況抽象出來,形成下面的SendMacro函數。 function TForm1.SendMacro(Name: String;Command: String): Boolean; var Macro: String; Cmd: array[0..255] of Char; begin Result := True; if Name <> '' then begin Macro := Format('['+Command+'(%s)]', [Name]) + #13#10; StrPCopy (Cmd, Macro); DDEClient.OpenLink; if not DDEClient.ExecuteMacro(Cmd, False) then Result := False; DDEClient.CloseLink; end; end; 過程首先利用Format函數形成宏字元串: Macro := Format('['+Command+'(%s)]', [Name]) + #13#10; 而後把Pascal型式的字元串拷貝到一個過程管理器可接受的PChar型式字元串中。 DDE聯接採用人工模式。首先呼叫OpenLink方法。而後呼叫ExecuteMacro方法發送命令,如失敗則返回False。最後用CloseLink關閉聯接。 三個按鈕CreateButton、AddButton、DeleteButton分別用於建立程式群群組、加入程式項目、解除程式群群組。它們的程式實現大同小異,如下所示。 建立程式群群組: procedure TForm1.CreateButtonClick(Sender: TObject); var Name: String; begin Name := InputBox('Input Box','Input Group Name',''); if Name = '' then MessageDlg('Group name can not be blank.', mtError, [mbOK], 0) else if SendMacro(Name,'CreateGroup') = False then MessageDlg('Unable to create group.', mtInformation, [mbOK], 0); end; 加入程式項目: procedure TForm1.AddButtonClick(Sender: TObject); var Name: String; begin Name := InputBox('Input Box','Input Application full_Path name',''); if Name = '' then MessageDlg('Application name can not be blank.', mtError, [mbOK], 0) else if SendMacro(Name,'AddItem') = False then MessageDlg('Unable to Add Item.', mtInformation, [mbOK], 0); end; 解除程式群群組: procedure TForm1.DeleteButtonClick(Sender: TObject); var Name: String; begin Name := InputBox('Input Box','Input Group Name to be Deleted',''); if Name = '' then MessageDlg('Group name can not be blank.', mtError, [mbOK], 0) else if SendMacro(Name,'DeleteGroup') = False then MessageDlg('Unable to create group.', mtInformation, [mbOK], 0); end; 7.4 DDE伺服器程式的實現 DDE伺服器程式響應DDE用戶的請求,一般地它包含了用戶程式希望獲取的數據。 建立一個DDE伺服器程式,必須要把一個DDEServerItem部件加入到窗體中。DDEServerItem的text或Lines屬性包含了要聯接的數據。一般地 DDEServerItem部件又和另一個文本控件相聯繫。當文本控件中的內容變化時則更新DDEServerItem 的text或Lines屬性的值。下面的一段程式把DDEServerItem和一個列示方塊相聯繫。這一聯繫是在列示方塊的OnChange事件中實現。 procedure Form1.OnListBoxChange(Sender: TObject); begin DDEServerItem1.Lines := ListBox1.Items; end; 建立DDE伺服器程式時也可以再加入一個DDEServerConv部件,並把兩個部件利用DDEServerItem的ServerConv屬性聯繫起來。此時DDE主題成為部件DDEServerConv的標簽,而不是擁有DDEServerItem部件窗體的標題。 在下列情況下使用DDEServerConv部件成為必要: 1.擁有DDEServerItem 部件窗體的標題可能在執行時改變或可能有其它窗體擁有同樣的標題。在這種情況下DDE聯接可能無法建立; 2.DDE用戶程式可能會向你的伺服器程式發送一條宏命令。在這種情況下只有擁有一個DDEServerConv部件才能響應OnMacroExecute事件並執行相應的動作。 7.4.1 和DDE用戶程式建立聯接 一般說來,建立DDE聯接是用戶程式的任務。但伺服器程式可以把一個聯接拷貝到剪貼簿上供用戶程式貼上去並建立DDE交談。步驟如下: 1.呼叫DDEServerItem部件的CopyToClipboard方法, 把Text(或Lines)屬性的值和DDE聯接資訊拷貝到剪貼簿上; 2.DDE用戶程式插入聯接的數據。一般地這是通過選擇適當的命令(如Edit|Paste Special或Edit|Paste Link)來實現的。 7.4.2 響應DDE事件 部件DDEServerConv有三個事件:OnOpen、OnClose、OnExecuteMacro。前兩個事件在DDE交談建立和終止時觸發。同(7.3.7)中的介紹。 OnExecuteMacro事件用於響應用戶程式發送過來的巨集指令。OnExecuteMacro事件處理過程有一個Msg參數,存檔發送過來的指令串。用戶可以在該過程中決定如何響應這些巨集指令。 DDEServerItem部件只有一個事件OnPokeData。這一事件用於響應用戶程式發送來的數據。如果用戶程式是Delphi程式,則用戶程式呼叫了PokeData或PokeDataLines方法。在這一事件的處理過程中用戶可以把發送來的數據存檔到一個合適的地方。一般說來這應該就是DDEServerItem所聯繫的文本控件。 下面的程式把發送來的數據存檔到ListBox中。 procedure Form1.OnDDEServerItemPokeData(Serder: TObject); begin ListBox1.Items := DDEServerItem1.Lines; end; 7.4.3 DDE伺服器應用例程 下面我們建立一個DDE伺服器例程和一個相應的DDE用戶例程。 DDE伺服器例程可以完成的工作有: 1.把DDE聯接資訊拷貝到剪貼簿上供其它程式使用; 2.利用一個TMemo部件為其它程式提供數據源; 3.接收用戶程式發送來的數據; 4.根據用戶程式發送來的巨集指令改變自身的執行狀態。 其中各部件的關鍵屬性如下: DDESrvrForm.ActiveControl = Memo1 DDESrvrForm.Menu = MainMenu1 Bevel1.Align = alTop Memo1.Align = alClient DDETestItem.ServerConv = DDETestTopic 通過設定Bevel1、Memo1的Align屬性,可以保證視窗大小變化時仍能有較為美觀的螢幕顯示。 Memo1是伺服器的數據源,DDE項目部件DDETestItem通過Memo1的OnChange事件與Memo1 建立聯繫。 procedure TDdeSrvrForm.doOnChange(Sender: TObject); begin if not FInPoke then DDETestItem.Lines := Memo1.Lines; end; 其中FInPoke是一個布爾型式的私有數據成員,用於標誌程式是否在處理用戶程式的數據發送。當數據是由用戶發送過來轉存到數據源時,則沒有必要再把數據傳給DDE項目部件。 把聯接資訊拷貝到剪貼簿,只需簡單呼叫DDETestItem的CopyToClipboard方法。 procedure TDDESrvrForm.CopyClick(Sender: TObject); begin DDETestItem.CopyToClipboard; end; 這是通過選擇表項Edit|Copy來呼叫的。 接收用戶程式發送來的數據,是在DDETestItem的OnPokeData事件處理過程中。在接收過程中改變FInPoke的值,以阻止數據的無效反送。 procedure TDDESrvrForm.doOnPoke(Sender: TObject); begin FInPoke := True; Memo1.Lines := DDETestItem.Lines; FInPoke := False; end; 在DDE交談部件DDETestTopic的OnExecuteMacro事件處理過程中處理用戶發送來的巨集指令。我們共定義了五種可以響應的巨集指令:CopyDDE、Clear、WS_Normal、WS_MINIMIZED、WS_MAXIMIZED,分別用於拷貝聯接資訊、清除Memo1中的內容以及改變視窗顯示狀態。 procedure TDdeSrvrForm.doMacro(Sender: TObject;Msg: TStrings); var Cmd: String; i: Integer; begin Cmd := ''; if Msg.Count = 0 then Exit; for I := 0 to Msg.Count-1 do begin Cmd := Msg.Strings[i]; if UpperCase(Cmd) = 'COPYDDE' then DDETestItem.CopyToClipboard else if UpperCase(Cmd) = 'CLEAR' then Memo1.text: = '' else if UpperCase(Cmd) = 'WS_NORMAL' then WindowState := wsNormal else if UpperCase(Cmd) = 'WS_MINIMIZED' then WindowState := wsMinimized else if UpperCase(Cmd) = 'WS_MAXIMIZED' then WindowState := wsMaximized else MessageDlg('Invalid Command',mtWarning,[mbOK],0); end; end; 下面的DDE用戶程式,主要是為了驗證上面的DDE伺服器程式而設計的,但同時也提供了一個DDE用戶程式設計的完整實例。
程式把接收到的 DDE數據存檔在一個TMemo類部件DDEDat中,而欲發送給伺服器的數據和巨集指令在另一個TMemo類部件PokeDat中輸入。兩個按鈕PokeBtn、ExecuteBtn用於發送數據和巨集指令。兩個選擇表項File|New Link和Edit|Paste Link用於根據用戶的輸入建立新聯接和從剪貼簿上貼上去DDE聯接。DDE聯接的建立通過呼叫SetLink方法實現。 建立新聯接的實現代碼如下。 procedure TFormD.doNewLink(Sender: TObject); begin DDEClient.SetLink (AppName.Text, TopicName.Text); DDEClientItem.DdeConv := DDEClient; DDEClientItem.DDEItem := ItemName.Text; end; 通過從剪貼簿貼上去聯接資訊來建立聯接的實現代碼如下。 procedure TFormD.Edit1Click(Sender: TObject); var Service, Topic, Item : String; begin PasteLink1.Enabled := GetPasteLinkInfo (Service, Topic, Item); end; procedure TFormD.doPasteLink(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; 在 DDEClientItem的OnChange事件處理過程中把接收到的事件存檔在DDEDat中。procedure TFormD.DDEClientItemChange(Sender: TObject); begin DDEDat.Lines := DDEClientItem.Lines; end; 數據發送的實現代碼如下。 procedure TFormD.doPoke (Sender: TObject); var DDECli : TDDEClientConv; begin DDECli := DDEClientItem.DdeConv; if DdeCli <> nil then DDECli.PokeDataLines (DDEClientItem.DDEItem, PokeDat.Lines); end; 巨集指令發送的實現代碼如下。 procedure TFormD.doMacro (Sender: TObject); var DDECli: TDDEClientConv; Cmd: String; begin DDECli := DDEClientItem.DdeConv; if DDECli <> nil then begin Cmd := PokeDat.Text + #13#10; DDECli.ExecuteMacroLines (PokeDat.Lines, True); end; end; 執行以上兩個程式,建立DDE聯接。經測試,數據傳輸、數據發送和巨集指令的發送與執行都達到預期要求。 7.4.4 小結 剪貼簿和DDE是Windows下數據通信的兩種方法。Delphi以簡便、友好的方式實現了相應的功能,為用戶程式設計提供了方便。一般說來,剪貼簿多用於靜態數據傳輸,而DDE用於動態數據交換、控制其它程式執行等場合。這些內容對於多用戶環境下的程式開發是很重要的
|
後一頁 前一頁 回目錄 回首頁 |