窗口創(chuàng)建時,把該窗口登錄為剪貼板觀察器,添加到剪貼板觀察器鏈中,同時進行變量、部件和剪貼板的初始化。
procedure TClipSaveForm.FormCreate(Sender: TObject);
begin
View := False;
SpeedButton2.Down := True;
MyBitmap := TBitmap.create;
try
MyBitmap.Width := 0;
MyBitmap.Height := 0 ;
except
Application.terminate;
end;
Clipboard.Clear;
NextViewerHandle := SetClipboardViewer(Handle);
end;
窗口關(guān)閉時,退出剪貼板觀察器鏈,并釋放內(nèi)存:
procedure TClipSaveForm.FormDestroy(Sender: TObject);
begin
ChangeClipboardChain(Handle,NextViewerHandle);
MyBitmap.Free;
end;
在以上兩段程序中用到的兩個Windows API函數(shù)SetClipboardViewer和ChangeClipboardChain分別用于登錄和退出剪貼板觀察器鏈。
程序保存位圖的功能是在消息響應過程WMDrawClipboard中實現(xiàn)的。該過程在剪貼板內(nèi)容有變化時被調(diào)用。
procedure TClipSaveForm.WMDrawClipboard(var Msg: TWMDrawClipboard);
var
FileName: String;
begin
If NextViewerHandle <> 0 then
SendMessage(NextViewerHandle,msg.Msg,0,0);
If ClipBoard.HasFormat(CF_BITMAP) then
begin
MyBitmap.Assign(Clipboard);
If SaveDialog1.Execute then
begin
FileName := SaveDialog1.FileName;
MyBitmap.SaveToFile(FileName);
end;
If View then
begin
WindowState := wsNormal;
Image1.Picture.Bitmap := MyBitmap;
end;
end;
Msg.Result := 0;
end;
程序首先判斷在剪貼板觀察器鏈中是否還存在下一個觀察器。如果有,則把消息傳遞下去,這是剪貼板觀察器程序的義務。而后判斷剪貼板上內(nèi)容的格式是否為位圖。如是,則首先把剪貼板上內(nèi)容保存到數(shù)據(jù)成員MyBitmap中,并激活一個文件保存對話框把位圖保存到文件中。如果View=True,則把窗口狀態(tài)(WindowState)設(shè)置為wsNormal,并把MyBitmap賦給Image部件的相應值,使用戶可以對剪貼板上的位圖進行觀察。
消息響應過程WMChangeCBChain在剪貼板觀察器鏈上其它觀察器退出時被調(diào)用。根據(jù)被移出觀察器的不同位置決定了不同的處理方法。
procedure TClipSaveForm.WMChangeCBChain(var Msg: TWMChangeCBChain);
begin
if Msg.Remove = NextViewerHandle then
NextViewerHandle := Msg.Next
else
if NextViewerHandle <> 0 then
SendMessage(NextViewerHandle,Msg.Msg,Msg.Remove,Msg.Next);
Msg.Result := 0;
end;
窗口上有兩個加速按鈕,兩個按鈕。它們擊鍵(click)事件處理過程如下。每一程序段的意義是非常顯然的。
procedure TClipSaveForm.Button1Click(Sender: TObject);
begin
Close;
end;
procedure TClipSaveForm.Button2Click(Sender: TObject);
begin
WindowState := wsMinimized;
end;
procedure TClipSaveForm.SpeedButton1Click(Sender: TObject);
begin
View := True;
Image1.Picture.Bitmap := MyBitmap;
end;
procedure TClipSaveForm.SpeedButton2Click(Sender: TObject);
begin
View := False;
Image1.Picture.Bitmap := nil;
end;
通過對這個程序的介紹,以下幾點是應該注意的:
1.提供了一種自己截獲和處理剪貼板上內(nèi)容的方法。讀者可以根據(jù)需要進一步擴充;
2.提供了響應Windows消息的方法。在第三篇有關(guān)自定義部件開發(fā)的內(nèi)容中,這一問題還要詳細論述;
3.最后的一點啟示是:在Delphi程序開發(fā)中巧妙應用傳統(tǒng)的Windows方法(如消息處理、 API函數(shù)等)仍是很有必要的。而在應用這些方法中所體現(xiàn)的方便之處,正是Delphi勝過其它可視化開發(fā)工具的一個重要方面。
7.2 Windows的DDE原理和 Dephi的DDE實現(xiàn)機制
7.2.1 Windows的DDE原理
Windows的DDE機制基于Windows的消息機制。兩個Windows應用程序通過相互之間傳遞DDE消息進行DDE會話(Conversation),從而完成數(shù)據(jù)的請求、應答、傳輸。這兩個應用程序分別稱為服務器(Server)和客戶(Client)。服務器是數(shù)據(jù)的提供者,客戶是數(shù)據(jù)的請求和接受者。
DDE會話由客戶程序啟動?蛻舫绦虬岩粭l消息(WM_DDE_INITIATE)傳播給當前運行的所有Windows程序。這條消息指明了客戶程序所需要的一般數(shù)據(jù)(應用程序、主題)。擁有這些數(shù)據(jù)的DDE服務器可以響應這條被傳播的消息。此時,DDE會話就開始了。
由于在每個主題中,DDE服務器可以支持一個或多個數(shù)據(jù)項,所以在客戶請求數(shù)據(jù)時應同時指明應用程序名、主題名和項目名。應用程序、主題、項目是DDE中三個最基本的概念。
利用Windows本身提供的DDE消息和API進行DDE編程是一件相當棘手的問題。 雖然使用DDE管理庫(ddeml.dll)可以一定程度上減輕開發(fā)者的工作負擔,但開發(fā)DDE程序仍不是一件輕松的事情。
此時Delphi出現(xiàn)了!Delphi通過其自身巧妙的設(shè)計使開發(fā)一個DDE應用程序同開發(fā)一個普通程序一樣地快捷、方便。
7.2.2 Delphi的DDE實現(xiàn)機制簡介
Delphi把所有的DDE功能做到四個部件中,它們是:
● TDDEClientConv : 用于客戶程序建立和維護一個DDE會話
● TDDEClientItem : 用于客戶程序建立和維護數(shù)據(jù)交換通道
● TDDEServerConv : 用于服務器程序響應DDE會話
● TDDEServerItem : 用于服務器程序維護數(shù)據(jù)交換通道
前兩個部件用于生成一個DDE客戶程序,后兩個部件用于生成一個DDE服務器程序。如果一個應用程序同時擁有這些部件,則這一程序既可以充當DDE客戶,也可以充當DDE服務器。
會話部件TDDEClientConv、TDDEServerConv用于建立和維護一個DDE會話。DDE會話包括DDE服務和DDE主題兩部分。
DDE服務是DDE服務器的名稱,即在一般的Windows DDE機制中所講的應用程序名。一般說來這一名稱是DDE服務器應用程序執(zhí)行文件名去掉 .EXE后綴。比如你的應用程序要和Word 6.0建立會話,則DDE服務為WINWORD。但也不盡然。比如你的應用程序要和Borland ReportSmith ( RPTSMITH.EXE ) 建立會話,則DDE 服務為 Report Smith。DDE服務到底如何,讀者可參看相關(guān)的DDE服務器應用程序文檔。
DDE主題是一個包含了聯(lián)接信息的數(shù)據(jù)單元。一般說來DDE 主題是一個包括擴展名的完整文件名。例如和Excel中的一個文件建立DDE會話,則主題可能是
Topic = 'c:\excel\Example\sale.xls'
如果服務器是一個Delphi應用程序,缺省情況下主題是包含欲聯(lián)接數(shù)據(jù)窗體的標題。如果服務器使用了DDEServerConv部件,則要求使用部件DDEServerConv的名稱作為DDE主題。
項目部件TDDEclientItem、TDDEServerItem用于建立和維護DDE數(shù)據(jù)的傳輸通道。 DDE項目中包含著實際欲傳輸?shù)臄?shù)據(jù)。DDE項目的格式取決于DDE服務器應用程序。一個可能的DDE項目例子是電子表格中的單元和數(shù)據(jù)庫表中的域。如果服務器是Delphi應用程序,則項目是連接的 DDEServerItem部件的名稱。
Delphi的DDE實現(xiàn)機制方便、實用,但也有一個令人遺憾的缺陷:只能傳輸文本數(shù)據(jù)以及命令、宏,而不能傳輸圖像數(shù)據(jù)。在這一點上微軟公司推出的Visual Basic 要略勝一籌。不過在目前文本數(shù)據(jù)的使用仍是最廣泛的,而且圖像傳輸可以利用剪貼板和OLE來實現(xiàn),則這一缺陷也并無很大的影響
7.3 DDE客戶程序的實現(xiàn)
DDE客戶程序啟動DDE會話,向服務器請求并從服務器接收數(shù)據(jù)。同時還可以向服務器發(fā)送數(shù)據(jù)、命令、宏,改變服務器的狀態(tài)并控制服務器的運行。
7.3.1 聯(lián)接模式(ConnectMode)
Delphi的DDE提供了兩種聯(lián)接模式:自動和人工。這可以通過DDEClinetConv 部件的ConnectMode屬性進行設(shè)置。如下表所示。
表 7.5 DDE的聯(lián)接模式
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
值 意 義
───────────────────────────────
ddeAutomatic 在運行中當包含TDDEClientConv部件的窗口創(chuàng)建時
聯(lián)接自動建立
ddeManual 只有當調(diào)用OpenLink方法時聯(lián)接才建立
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
不同聯(lián)接模式,DDE客戶程序的實現(xiàn)方式不同。
對于自動模式:
1. 向窗體中加入DDEClientConv和DDEClientItem部件并命名;
2. 把DDEClientItem部件的DdeConv屬性設(shè)置為DDEClientConv部件的名稱;
如果在設(shè)計時建立,則通過對象觀察器進行選擇;如果在運行時建立聯(lián)系, 則通過如下的一條語句設(shè)置屬性的值:
DDEClientItem1.DdeConv := 'DDEClientConv1' ;
3. 和服務器建立聯(lián)系,實現(xiàn)數(shù)據(jù)共享。
對于人工模式:
1.向窗體中加入DDEClientConv部件;
2.和服務器建立聯(lián)系;
3. 數(shù)據(jù)更新時調(diào)用RequestData方法申請并獲得數(shù)據(jù)。
7.3.2 和DDE服務器建立聯(lián)系
和DDE服務器建立聯(lián)系,既可以在設(shè)計時進行,也可以在運行時進行。
在設(shè)計時,DDE聯(lián)接可以通過剪貼板進行粘貼。具體步驟如下:
1. 激活服務器程序,并選中你的客戶程序欲聯(lián)接的數(shù)據(jù);
2. 把數(shù)據(jù)和DDE聯(lián)接信息拷貝到剪貼板上。一般說來這只需要選擇服務器應用程序的 Edit|Copy 菜單;
3. 在Delphi IDE的設(shè)計窗體中選中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聯(lián)接的正確值。
對于人工模式以下步驟是不需要的。
7.選中DDEClientItem部件,并在Object Inspector中設(shè)置DdeConv屬性為已完成聯(lián)接的DDEClientConv部件名稱;
8.假如剪貼板上的DDE 聯(lián)接信息仍保留的話,從Object Inspector的下拉列表框中選擇 DDEItem 屬性的值。否則輸入正確的值。
在運行時,調(diào)用 SetLink 方法來建立DDE聯(lián)接。
SetLink有兩個String類型的參數(shù),分別用來接受DDEService和DDETopic的值。過程執(zhí)行后DDEClientConv部件的DDEService 和DDeTopic屬性被設(shè)置。要注意的是:在運行時直接設(shè)置DDEService和DDETopic的值并不能建立一個DDE聯(lián)接,而必須調(diào)用SetLink 方法進行初始化。
比如,下面的語句和Excel的System主題建立聯(lián)接:
DDEClietnConv. SetLink('Excel','System');
調(diào)用SetLink方法后,還需要設(shè)置DDEClientItem部件的DDEItem屬性。
比如,下面的語句聯(lián)接Excel的Topics項目,用以獲取當前活躍文件的文件名:
DDEClietnItem.DDEItem := 'Topics';
當DDE聯(lián)接建立后,聯(lián)接的數(shù)據(jù)保存在DDEClientItem部件的Text和Lines 屬性中,Text用于保存一個字符串(String),Lines用于保存一個字符串鏈表(TStrings)對象。
為了顯示聯(lián)接數(shù)據(jù),可以在DDEClientItem的OnChange事件中把數(shù)值賦給一個可視部件。
下面的事件過程把聯(lián)接數(shù)據(jù)實時地顯示在一個編輯框中。
procedure Form1.DDEClientItemChange(Sender: Tobject);
begin
Edit1.Text := DDEClientItem1.Text;
end;
運行狀態(tài)下也可以從剪貼板上粘貼DDE聯(lián)接信息,并調(diào)用SetLink建立DDE會話。下面的例子顯示了當用戶按下應用程序中的Paste Link按鈕時,動態(tài)建立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聯(lián)接信息保存在三個參數(shù)中;如果返回False,說明剪貼板上沒有正確格式的DDE聯(lián)接信息。
7.3.3 數(shù)據(jù)申請
雖然自動模式快捷、方便,但仍有一些理由使用DDE的人工模式:
1.服務器程序可能不支持自動數(shù)據(jù)傳輸,客戶必須顯式申請服務器更新一個特定的項目;
2.節(jié)省通信費用。假如沒有實時傳輸?shù)囊螅瑒t人工模式可以大幅度降低通信的開銷;
3.若客戶程序只用于控制服務器的運行,則往往沒有必要使用自動模式。
人工模式下客戶程序的數(shù)據(jù)更新需要采用數(shù)據(jù)申請的方式。數(shù)據(jù)申請需要調(diào)用DDEClientConv部件的RequestData方法。RequestData有一個參數(shù),指向要申請的DDE項目。RequestData返回一個Pchar類型的無結(jié)束符字符串,包含了申請到的文本。返回字符串占用的內(nèi)存必須在程序終止前顯式釋放。
在人工模式下,即使存在一個DDEClientItem部件且與DDEClientConv相聯(lián)接,數(shù)據(jù)更新后DDEClientItem部件的Text、Lines屬性的值也不會改變。
相關(guān)推薦:2010年9月計算機等級考試試題及答案解析專題北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內(nèi)蒙古 |