1、第七章剪贴板和动态数据交换一应用程序间的数据交换是象Windows 这样的多任务环境的重要特性。作为一种基于Windows的开发工具,Delphi支持如下四种数据交换方式:剪贴板、动态数据交换 ( DDE)、对象联接与嵌入(OLE)以及动态联接库(DLLs)。这中间前三种方式最为常用,OLE功能最为强大,DDE次之。而剪贴板使用最为方便。在本章,我们只讨论剪贴板和动态数据交换。利用OLE实现数据交换见下一章,利用动态联接库(DLLs)进行数据交换将在第十章中介绍。7.1 剪贴板及其应用 本质上,剪贴板只是一个全局内存块。当一个应用程序将数据传送给剪贴板后,通过修改内存块分配标志,把相关内存块的
2、所有权从应用程序移交给Windows自身。其它应用程序可以通过一个句柄找到这个内存块,从而能够从内存块中读取数据。这样就实现了数据在不同应用程序间的传输。 剪贴板虽然功能较为简单,且不能实现实时传输,但却是更为复杂的DDE和OLE的基础。对于一些只是偶尔需要使用其它应用程序数据的程序来说,使用剪贴板不失为一种方便、快捷的方式。 Delphi把剪贴板的大部分功能封装到一个TClipboard类中,同时把使用频度最高的文本传输功能(包括DBImage的图像传输功能)置入相应部件作为部件的方法,从而使用户可以十分方便地使用剪贴板进行编程。7.1.1 使用剪贴板传输文本剪贴板传输文本主要是应用如下的三
3、个方法:CopyToClipboard、CutToClipboard 和PasteFromClipboard。包含这些方法的部件如下表所示。 表7.1 包含剪贴板方法的部件方 法 部 件TDBEditTDBMemoTDBImageCopyToClipboard TEdit TMemo TMaskEditTOLEContainerTDDEServerItemTDBEdit TDBMemoCutToClipboard TDBImageTEdit TMemo TMaskEditTDBEdit TDBMemoPasteFromClipboard TDBImageTEdit TMemo TMaskEdi
4、t 除TDBImage外,其余全是有关文本的控件。 在把文本传输到剪贴板之前,文本必须被选中。 若选TMaskEdit的AutoSelect属性为True,则当MaskEdit获得输入焦点时文本自动被选中;若选TEdit、TMemo的HideSelection属性为True,则失去焦点时,文本选中状态自动隐藏,重新获得焦点时再显示。下面的语句把MaskEdit中选中的文本剪切到剪贴板:MaskEdit .CutToClipboard;下面的语句把剪贴板中的文本粘贴到Memo的当前光标处:Memo.PasteFromClipboard;利用剪贴板类也可以实现文本的传输,见(7.1.2)中的介绍。
5、7.1.2 剪贴板类 为方便剪贴板的操作,Delphi在Clipbrd库单元中定义了一个TClipboard类,并且预定义了一个变量Clipboard作为类TClipboard的实例,从而使用户在绝大多数场合不必自己去定义一个TClipboard的实例。 利用剪贴板类可以进行文本、图像和部件的传输,剪贴板类为实现这些方法提供了相应的属性和方法。表7.2、表7.3列出了TClipboard属性和方法的意义。表 7.2 TClipboard的属性属 性 意 义AsText 保存剪贴板的文本,只有运行时才可设置FormatCount 可用剪贴板格式的数目Formats 可用剪贴板格式链表 7.3 T
6、Clipboard的方法方 法 参 数 意 义Clear 无 清除剪贴板的内容Assign Source:TPersistent 把Source参数指定的对象拷贝到剪贴板,常用于图形、图像对象Open 无打开剪贴板,阻止其它应用程序改变它的内容Close 无 关闭打开的剪贴板SetComponent Source:TPersistent 把部件拷贝到剪贴板GetComponent Owner 从剪贴板取回一个部件并放置Parent :TPersistentSetAsHandle Format:Word 把指定格式数据的句柄交给剪贴板返回类型:THandleGetAsHandle Format:
7、Word 返回剪贴板指定格式数据的句柄返回类型:THandleHasFormat Format:Word 判断剪贴板是否拥有给定的格式返回类型:BooleanSetTextBuf Buffer:PChar 设置剪贴板的文本内容剪贴板中可能的数据格式如下表。表 7.4剪贴板数据格式及其意义数据格式 意 义CF_TEXT 文本。每行以CF_LF结束,nil标志文本结束CF_BITMAP Windows位图CF_METAFILE Windows元文件 CF_PICTURE TPicture类型的对象CF_OBJECT 任何TPersistent类型的对象利用TClipboard实现文本的传输使用As
8、Text属性和SetTextBuf方法。AsText属性为非控件部件的剪贴板操作提供了方便。如:Clipboard. AsText := Form1.Caption ;把Form1的标题拷贝到剪贴板。Label1.Caption := Clipboard.AsText;把剪贴板中的文本写入Label1。SetTextBuf用于把超过255个字符的字符串拷入剪贴板。7.1.3 利用剪贴板传输图像7.1.3.1 拷贝Image部件上的内容和窗体上的图形可以直接拷贝到剪贴板。图像拷贝利用Clipboard的Assign方法。例如:Clipboard.Assign(Image1.Picture);把I
9、mage1上的图像拷贝到剪贴板。7.1.3.2 剪切图像的剪切是首先把图像拷贝到剪贴板,而后在原位置用空白图像进行覆盖。下面一段程序表示了图像的剪切。 procedure TForm1.Cut1Click(Sender: TObject);varARect: TRect;beginClipboard.Assign(Image1.Picture);with Image.Canvas dobeginCopyMode := cmWhiteness; ARect := Rect(0, 0, Image.Width, Image.Height); CopyRect(ARect, Image.Canvas
10、, ARect); CopyMode := cmSrcCopy; end;end;7.1.3.3 粘贴从剪贴板上粘贴图像,首先检测剪贴板上的数据格式。如果格式为CF_BITMAP,则调用目标位图的Assign 方法粘贴图像。程序清单如下。procedure TForm1.PasteButtonClick(Sender: TObject);varBitmap: TBitmap;beginif Clipboard.HasFormat(CF_BITMAP) then beginBitmap := TBitmap.Create; tryBitmap.Assign(Clipboard); Image.C
11、anvas.Draw(0, 0, Bitmap); finallyBitmap.Free;end;end;end;try.finally为资源保护块,参第十二章。7.1.4 建立自己的剪贴板观察程序 在这一节中我们要建立一个自己的剪贴板观察程序,用来保存截获到剪贴板中的位图。 Windows允许用户建立自己的剪贴板观察程序,并把该程序添加到一个剪贴板观察器链中。在链中,位置靠前的程序有义务把有关剪贴板的消息传递到紧随其后的观察程序。而处于链首的程序由Windows的消息循环机制直接把剪贴板消息发送过来。 建立一个剪贴板观察程序,首先该程序必须能响应相应的Windows消息。对于那些熟悉Micr
12、osoft公司Visual Basic的读者来说,这是令他们头疼而束手无策的地方。但Delphi在这方面却有良好的表现:利用关键字message,用户可以将一个过程定义为响应特定的Windows消息。如:procedure WMDrawClipboard(var Msg:TWMDrawClipboard);message WM_DRAWCLIPBOARD; 可以响应WM_DRAWCLIPBOARD消息。类TWMDrawClipboard是消息类Message 的子类。Delphi把所有的消息都重新进行了定义,使用户在使用时可以直接引用其便于记忆的数据成员,而不必再自己动手去分解消息。虽然这并不
13、能算作是一个重大的改进,但却体现了Delphi处处为用户方便着想的特点。 我们将要建立的程序目的是把截获到剪贴板上的位图保存下来。在本书的写作过程中,这一工作是大量存在的。虽然利用Windows工具PaintBrush(画笔),通过粘贴、保存等操作可以实现这一功能,但却存在以下一些问题:1.程序频繁切换影响效率,当有大量位图存在时更是如此;2.画笔有一个很讨厌的缺陷:当剪贴板上的位图比画笔界面的客户区大时,客户区外的位图被截断。因而往往需要根据所截获位图的大小来调整画笔客户区的大小,并重新进行粘贴。而如果开始就把画笔客户区调整到足够大,又会覆盖掉屏幕上一些有用的信息。为解决这些问题,我开发了下
14、面的程序。程序启动时,以极小化方式运行。此时只要剪贴板中存入位图,则自动弹出一个对话框请求用户保存。如果用户希望查看确认,则可以双击运行程序图标,选择相应按钮,剪贴板中的位图就会显示在屏幕上。部件关键属性设计如下:ClipSaveForm:CaptionSave Bitmap in Clipboard Panel1:Align = Top Image1:Align = Client SaveDialog1:FileEditStyle = fsEditFileName = *.bmpFilter = Bitmap Files(*.bmp)|*.bmp|Any Files(*.*)|*.*Init
15、ialDir = c:bmpTitle = Save Bitmap 程序主窗口是TForm派生类TClipSaveForm的实例。TClipSaveForm通过定义一些私有数据成员和过程,使响应和处理Windows的相应消息成为可能。下面是TClipSaveForm的类定义:typeTClipSaveForm = class(TForm)SaveDialog1: TSaveDialog;Image1: TImage;Panel1: TPanel;Button1: TButton;SpeedButton1: TSpeedButton;SpeedButton2: TSpeedButton;Butt
16、on2: TButton;procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);procedure Button1Click(Sender: TObject);procedure Button2Click(Sender: TObject);procedure SpeedButton1Click(Sender: TObject);procedure SpeedButton2Click(Sender: TObject);private Private declarations MyBitmap: T
17、Bitmap; 保存截获的位图 View: Boolean; 判断是否显示 NextViewerHandle: HWND; 下一剪贴板观察器的句柄 procedure WMDrawClipboard(var Msg:TWMDrawClipboard);message WM_DRAWCLIPBOARD;procedure WMChangeCBChain(var Msg:TWMChangeCBChain);message WM_CHANGECBCHAIN; 响应Windows的剪贴板消息 public Public declarations end; 窗口创建时,把该窗口登录为剪贴板观察器,添加到
18、剪贴板观察器链中,同时进行变量、部件和剪贴板的初始化。procedure TClipSaveForm.FormCreate(Sender: TObject);beginView := False;SpeedButton2.Down := True;MyBitmap := TBitmap.create;tryMyBitmap.Width := 0;MyBitmap.Height := 0 ;exceptApplication.terminate;end;Clipboard.Clear;NextViewerHandle := SetClipboardViewer(Handle);end;窗口关闭时
19、,退出剪贴板观察器链,并释放内存:procedure TClipSaveForm.FormDestroy(Sender: TObject);beginChangeClipboardChain(Handle,NextViewerHandle);MyBitmap.Free;end;在以上两段程序中用到的两个Windows API函数SetClipboardViewer和ChangeClipboardChain分别用于登录和退出剪贴板观察器链。程序保存位图的功能是在消息响应过程WMDrawClipboard中实现的。该过程在剪贴板内容有变化时被调用。procedure TClipSaveForm.W
20、MDrawClipboard(var Msg: TWMDrawClipboard);varFileName: String;beginIf NextViewerHandle 0 thenSendMessage(NextViewerHandle,msg.Msg,0,0);If ClipBoard.HasFormat(CF_BITMAP) thenbeginMyBitmap.Assign(Clipboard);If SaveDialog1.Execute thenbeginFileName := SaveDialog1.FileName;MyBitmap.SaveToFile(FileName);
21、end;If View thenbeginWindowState := wsNormal;Image1.Picture.Bitmap := MyBitmap;end;end;Msg.Result := 0;end; 程序首先判断在剪贴板观察器链中是否还存在下一个观察器。如果有,则把消息传递下去,这是剪贴板观察器程序的义务。而后判断剪贴板上内容的格式是否为位图。如是,则首先把剪贴板上内容保存到数据成员MyBitmap中,并激活一个文件保存对话框把位图保存到文件中。如果View=True,则把窗口状态(WindowState)设置为wsNormal,并把MyBitmap赋给Image部件的相应值,
22、使用户可以对剪贴板上的位图进行观察。消息响应过程WMChangeCBChain在剪贴板观察器链上其它观察器退出时被调用。根据被移出观察器的不同位置决定了不同的处理方法。procedure TClipSaveForm.WMChangeCBChain(var Msg: TWMChangeCBChain);beginif Msg.Remove = NextViewerHandle thenNextViewerHandle := Msg.Nextelseif NextViewerHandle 0 thenSendMessage(NextViewerHandle,Msg.Msg,Msg.Remove,M
23、sg.Next);Msg.Result := 0;end;窗口上有两个加速按钮,两个按钮。它们击键(click)事件处理过程如下。每一程序段的意义是非常显然的。procedure TClipSaveForm.Button1Click(Sender: TObject);beginClose;end;procedure TClipSaveForm.Button2Click(Sender: TObject);beginWindowState := wsMinimized;end;procedure TClipSaveForm.SpeedButton1Click(Sender: TObject);be
24、ginView := True;Image1.Picture.Bitmap := MyBitmap;end;procedure TClipSaveForm.SpeedButton2Click(Sender: TObject);beginView := False;Image1.Picture.Bitmap := nil;end;通过对这个程序的介绍,以下几点是应该注意的:1.提供了一种自己截获和处理剪贴板上内容的方法。读者可以根据需要进一步扩充;2.提供了响应Windows消息的方法。在第三篇有关自定义部件开发的内容中,这一问题还要详细论述;3.最后的一点启示是:在Delphi程序开发中巧妙应
25、用传统的Windows方法(如消息处理、 API函数等)仍是很有必要的。而在应用这些方法中所体现的方便之处,正是Delphi胜过其它可视化开发工具的一个重要方面。7.2 Windows的DDE原理和 Dephi的DDE实现机制7.2.1 Windows的DDE原理 Windows的DDE机制基于Windows的消息机制。两个Windows应用程序通过相互之间传递DDE消息进行DDE会话(Conversation),从而完成数据的请求、应答、传输。这两个应用程序分别称为服务器(Server)和客户(Client)。服务器是数据的提供者,客户是数据的请求和接受者。 DDE会话由客户程序启动。客户程
26、序把一条消息(WM_DDE_INITIATE)传播给当前运行的所有Windows程序。这条消息指明了客户程序所需要的一般数据(应用程序、主题)。拥有这些数据的DDE服务器可以响应这条被传播的消息。此时,DDE会话就开始了。 由于在每个主题中,DDE服务器可以支持一个或多个数据项,所以在客户请求数据时应同时指明应用程序名、主题名和项目名。应用程序、主题、项目是DDE中三个最基本的概念。 利用Windows本身提供的DDE消息和API进行DDE编程是一件相当棘手的问题。 虽然使用DDE管理库(ddeml.dll)可以一定程度上减轻开发者的工作负担,但开发DDE程序仍不是一件轻松的事情。 此时Del
27、phi出现了!Delphi通过其自身巧妙的设计使开发一个DDE应用程序同开发一个普通程序一样地快捷、方便。7.2.2 Delphi的DDE实现机制简介Delphi把所有的DDE功能做到四个部件中,它们是: TDDEClientConv : 用于客户程序建立和维护一个DDE会话 TDDEClientItem : 用于客户程序建立和维护数据交换通道 TDDEServerConv : 用于服务器程序响应DDE会话 TDDEServerItem : 用于服务器程序维护数据交换通道 前两个部件用于生成一个DDE客户程序,后两个部件用于生成一个DDE服务器程序。如果一个应用程序同时拥有这些部件,则这一程序
28、既可以充当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服务到底如何
29、,读者可参看相关的DDE服务器应用程序文档。 DDE主题是一个包含了联接信息的数据单元。一般说来DDE 主题是一个包括扩展名的完整文件名。例如和Excel中的一个文件建立DDE会话,则主题可能是Topic = c:excelExamplesale.xls 如果服务器是一个Delphi应用程序,缺省情况下主题是包含欲联接数据窗体的标题。如果服务器使用了DDEServerConv部件,则要求使用部件DDEServerConv的名称作为DDE主题。 项目部件TDDEclientItem、TDDEServerItem用于建立和维护DDE数据的传输通道。 DDE项目中包含着实际欲传输的数据。DDE项目的格式取决于DD
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1