⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 12. 剪贴簿.txt

📁 本书介绍了在Microsoft Windows 98、Microsoft Windows NT 4.0和Windows NT 5.0下程序写作的方法
💻 TXT
📖 第 1 页 / 共 4 页
字号:
        
MENUITEM "De&lete\tDel",                  IDM_EDIT_CLEAR
        
       MENUITEM SEPARATOR               
        
       MENUITEM "&Reset",                    IDM_EDIT_RESET
        
   END
        
END
        

/////////////////////////////////////////////////////////////////////////////
        
// Accelerator
        
CLIPTEXT ACCELERATORS DISCARDABLE
        
BEGIN
        
   "C",           IDM_EDIT_COPY,    VIRTKEY, CONTROL, NOINVERT
        
   "V",           IDM_EDIT_PASTE,    VIRTKEY, CONTROL, NOINVERT
        
   VK_DELETE,                    IDM_EDIT_CLEAR,    VIRTKEY, NOINVERT
        
   "X",           IDM_EDIT_CUT,     VIRTKEY, CONTROL, NOINVERT
        
END
        
RESOURCE.H(摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by ClipText.rc
        
#define IDM_EDIT_CUT           40001
        
#define IDM_EDIT_COPY          40002
        
#define IDM_EDIT_PASTE         40003
        
#define IDM_EDIT_CLEAR         40004
        
#define IDM_EDIT_RESET         40005
        
这是在Windows NT下执行Unicode版和ANSI版程序的概念,而且可以看到,剪贴簿是如何在两种字符集之间转换的。注意CLIPTEXT.C顶部的#ifdef叙述。如果定义了UNICODE标识符,那么CF_TCHAR(我命名的一种常用的剪贴簿格式)就等于CF_UNICODETEXT;否则,它就等于CF_TEXT。程序后面呼叫的IsClipboardFormatAvailable、GetClipboardData和SetClipboardData函数都使用CF_TCHAR来指定数据型态。

在程序的开始部分(以及您从「Edit」菜单中选择「Reset」选项时),静态变量pText包含一个指针,在Unicode版的程序中,指针指向Unicode字符串「Default Text -Unicode version」;在非Unicode版的程序中,指针指向「Default Text - ANSI version」。您可以用「Cut」或「Copy」命令将字符串传递给剪贴簿,用「Cut」或「Delete」命令从程序中删除字符串。「Paste」命令将剪贴簿中的文字内容复制到pText。在WM_PAINT消息处理期间,pText将字符串显示在程序的显示区域。

如果您先在Unicode版的CLIPTEXT中选择了「Copy」命令,然后在非Unicode版中选择「Paste」命令,那么您就能看到文字已经从Unicode转换成了ANSI。类似地,如果您执行相反的操作,那么文字就会从ANSI转换成Unicode。

复杂的剪贴簿用法


我们已经看到,在将数据准备好之后,从剪贴簿传输数据时需要四个呼叫:

OpenClipboard                      (hwnd) ;
        
EmptyClipboard                     () ;
        
SetClipboardData           (iFormat, hGlobal) ;
        
CloseClipboard                     () ;
        
存取这些数据需要三个呼叫

OpenClipboard (hwnd) ;
        
hGlobal = GetClipboardData (iFormat) ;
        
其它行程序
        
CloseClipboard () ;
        
在GetClipboardData和CloseClipboard呼叫之间,可以复制剪贴簿数据或以其它方式来使用它。很多应用程序都需要采用这种方法,但也可以用更复杂的方式来使用剪贴簿。

利用多个数据项


当打开剪贴簿并把数据传送给它时,必须先呼叫EmptyClipboard,通知Windows释放或删除剪贴簿上的内容。不能在现有的剪贴簿内容中附加其它东西。所以,从这种意义上说,剪贴簿每次只能保留一个数据项。

但是,可以在EmptyClipboard和CloseClipboard呼叫之间多次呼叫SetClipboardData,每次都使用不同的剪贴簿格式。例如,如果想在剪贴簿中储存一个很短的文字字符串,可以把这个文字写入metafile,也可以把这个文字写入位图。把位图选进内存设备内容中,并把这个字符串写进位图中。利用这种方法可以使字符串不仅能为从剪贴簿上读取文字的程序所使用,也可以为从剪贴簿上读取位图和metafile的程序所使用。当然,这些程序并不能知道metafile或位图实际上包含了一个字符串。

如果想把一些句柄写到剪贴簿上,对每个句柄均可以呼叫SetClipboardData:

OpenClipboard             (hwnd) ;
        
EmptyClipboard             () ;
        
SetClipboardData   (CF_TEXT, hGlobalText) ;
        
SetClipboardData   (CF_BITMAP, hBitmap) ;
        
SetClipboardData   (CF_METAFILEPICT, hGlobalMFP) ;
        
CloseClipboard             () ;
        
当这三种格式的数据同时位于剪贴簿上时,用CF_TEXT、CF_BITMAP或CF_METAFILEPICT参数呼叫IsClipboardFormatAvailable将传回TRUE。通过下列呼叫程序可以存取这些代码:

hGlobalText = GetClipboardData (CF_TEXT) ;
        

hBitmap = GetClipboardData (CF_BITMAP) ;
        

hGlobalMFP = GetClipboardData (CF_METAFILEPICT) ;
        
下一次程序呼叫EmptyClipboard时,Windows将释放或删除剪贴簿上保留的所有三个句柄。

在将不同的文字格式、不同的位图格式或者不同的metafile格式添加到剪贴簿时,不要使用这种技术。只使用一种文字格式、一种位图格式以及一种metafile格式。就像我所说的那样,Windows将在CF_TEXT、CF_OEMTEXT和CF_UNICODETEXT之间转换,也可以在CF_BITMAP和CF_DIB之间,以及在CF_METAFILEPICT和CF_ENHMETAFILE之间进行转换。

透过首先打开剪贴簿,然后呼叫EnumClipboardFormats,程序可以确定剪贴簿储存的所有格式。开始时设定变量iFormat为0:

iFormat = 0 ;
        
OpenClipboard (hwnd) ;
        
现在从0值开始逐次进行连续的EnumClipboardFormats呼叫。函数将为目前在剪贴簿中的每种格式传回一个正的iFormat值。当函数传回0时,表示完成:

while (iFormat = EnumClipboardFormats (iFormat))
        
{
        
   各个iFormat值的处理方式
        
}
        
CloseClipboard () ;
        
您可以通过下面的呼叫来取得目前在剪贴簿中之不同格式的个数:

iCount = CountClipboardFormats () ;
        
延迟提出


当把数据放入剪贴簿中时,一般来说要制作一份数据的副本,并将包含这份副本的内存块句柄传给剪贴簿。对非常大的数据项来说,这种方法会浪费内存空间。如果使用者不想把数据粘贴到另一个程序里,那么,在被其它内容取代之前,它将一直占据着内存空间。

通过使用一种叫做「延迟提出」的技术可以避免这个问题。实际上,直到另一个程序需要数据,程序才提供这份数据。为此,不将数据句柄传给Windows,而是在SetClipboardData呼叫中使用NULL:

OpenClipboard              (hwnd) ;
        
EmptyClipboard             () ;
        
SetClipboardData   (iFormat, NULL) ;
        
CloseClipboard             () ;
        
可以有多个使用不同iFormat值的SetClipboardData呼叫,对其中某些呼叫可使用NULL值。而对其他一些则使用实际的句柄值。

前面的过程比较简单,以下的过程就要稍微复杂一些了。当另一个程序呼叫GetClipboardData时,Windows将检查那种格式的句柄是否为NULL。如果是,Windows将给「剪贴簿所有者」(您的程序)发送一个消息,要求取得数据的实际句柄,这时您的程序必须提供这个句柄。

更具体地说,「剪贴簿所有者」是将数据放入剪贴簿的最后一个窗口。当一个程序呼叫OpenClipboard时,Windows储存呼叫这个函数时所用的窗口句柄,这个句柄标示打开剪贴簿的窗口。一旦收到一个EmptyClipboard呼叫,Windows就使这个窗口作为新的剪贴簿所有者。

使用延迟提出技术的程序在它的窗口消息处理程序中必须处理三个消息:WM_RENDERFORMAT、WM_RENDERALLFORMATS和WM_DESTROYCLIPBOARD。当另一个程序呼叫GetClipboardData时,Windows给窗口消息处理程序发送一个WM_RENDERFORMAT消息,wParam的值是所要求的格式。在处理WM_RENDERFORMAT消息时,不要打开或清空剪贴簿。为wParam所指定的格式建立一个整体内存块,把数据传给它,并用正确的格式和相应句柄呼叫SetClipboardData。很明显地,为了在处理WM_RENDERFORMAT时正确地构造出此数据,需要在程序中保留这些信息。当另一个程序呼叫EmptyClipboard时,Windows给您的程序发送一个WM_DESTROYCLIPBOARD消息,告诉您不再需要构造剪贴簿数据的信息。您的程序不再是剪贴簿的所有者。

如果程序在它自己仍然是剪贴簿所有者的时候就要终止执行,并且剪贴簿上仍然包含着该程序用SetClipboardData设定的NULL数据句柄,它将收到WM_RENDERALLFORMATS消息。这时,应该打开剪贴簿,清空它,把数据加载内存块中,并为每种格式呼叫SetClipboardData,然后关闭剪贴簿。WM_RENDERALLFORMATS消息是窗口消息处理程序最后收到的消息之一。它后面跟有WM_DESTROYCLIPBOARD消息(由于已经提出了所有数据),然后是正常的WM_DESTROY消息。

如果您的程序只能向剪贴簿传输一种格式的数据(例如文字),那么您可以把WM_RENDERALLFORMATS和WM_RENDERFORMAT处理结合在一起。这些程序代码应该类似下面这样:

case        WM_RENDERALLFORMATS :
        
           OpenClipboard (hwnd) ;
        
           EmptyClipboard () ;
        
                                                         // fall through
        
case        WM_RENDERFORMAT :
        
   // 将文字放入整体内存块
        
           SetClipboardData (CF_TEXT, hGlobal) ;
        
           if (message == WM_RENDERALLFORMATS)
        
                  CloseClipboard () ;
        
           return 0 ;
        
如果您的程序使用好几种剪贴簿格式,那么您可能想为wParam所要求的格式处理WM_ RENDERFORMAT。除非程序在存放构造数据所需的信息时遇到困难,否则不需要处理WM_DESTROYCLIPBOARD消息。

自订数据格式


到目前为止,我们仅处理了Windows定义的标准剪贴簿资料格式。但是,您可能想用剪贴簿来储存「自订数据格式」。许多文书处理程序使用这种技术来储存包含着字体和格式化信息的文字。

初看之下,这个概念似乎是没有意义的。如果剪贴簿的作用是在应用程序之间传送数据,那么,为什么剪贴簿中要含有只有一个应用程序才能理解的数据呢?答案很简单:剪贴簿允许在同一个程序的内部(或者可能在一个程序中的不同执行实体之间)传送数据。很明显地,这些执行实体能理解它们自己的自订数据格式。

有几种使用自订数据格式的方法。最简单的方法用到一种表面上是标准剪贴簿格式(文字、位图或metafile)的数据,可是该数据实际上只对您的程序有意义。这种情况下,在SetClipboardData和GetClipboardData呼叫中可使用下列wFormat值:CF_DSPTEXT、CF_DSPBITMAP、CF_DSPMETAFILEPICT或CF_DSPENHMETAFILE(字母DSP代表「显示器」)。这些格式允许Windows按文字、位图或metafile来浏览或显示资料。但是,另一个使用常规的CF_TEXT、CF_BITMAP、CF_DIB、CF_METAFILEPICT或CF_ENHMETAFILE格式呼叫GetClipboardData的程序将不能取得这个数据。

如果用其中一种格式把数据放入剪贴簿中,则必须使用同样的格式读出数据。但是,如何知道数据是来自程序的另一个执行实体,还是来自使用其中某种数据格式的另一个程序呢?这里有一种方法,可以透过下列呼叫首先获得剪贴簿所有者:

hwndClipOwner = GetClipboardOwner () ;
        
然后可以得到此窗口句柄的窗口类别名称:

TCHAR szClassName [32] ;
        
//其它行程序
        
GetClassName (hwndClipOwner, szClassName, 32) ;
        
如果类别名称与程序名称相同,那么数据是由程序的另一个执行实体传送到剪贴簿中的。

使用自订数据格式的第二种方法涉及到CF_OWNERDISPLAY旗标。SetClipboardData的整体内存句柄是NULL:

SetClipboardData (CF_OWNERDISPLAY, NULL) ;
        
这是某些文书处理程序在Windows的剪贴簿浏览器的显示区域中显示格式化文字时所采用的方法。很明显地,剪贴簿浏览器不知道如何显示这种格式化文字。当一个文书处理程序指定CF_OWNERDISPLAY格式时,它也就承担起在剪贴簿浏览器的显示区域中绘图的责任。

由于整体内存句柄为NULL,所以用CF_OWNERDISPLAY格式(剪贴簿所有者)呼叫SetClipboardData的程序必须处理由Windows发往剪贴簿所有者的延迟提出消息、以及5条附加消息。这5个消息是由剪贴簿浏览器发送到剪贴簿所有者的:

WM_ASKCBFORMATNAME剪贴簿浏览器把这个消息发送到剪贴簿所有者,以得到数据格式名称。lParam参数是指向缓冲区的指标,wParam是这个缓冲区能容纳的最大字符数目。剪贴簿所有者必须把剪贴簿数据格式的名字复制到这个缓冲区中。
  
WM_SIZECLIPBOARD这个消息通知剪贴簿所有者,剪贴簿浏览器的显示区域大小己发生了变化。wParam参数是剪贴簿浏览器的句柄,lParam是指向包含新尺寸的RECT结构的指针。如果RECT结构中都是0,则剪贴簿浏览器退出或最小化。尽管Windows的剪贴簿浏览器只允许它自己的一个执行实体执行,但其它剪贴簿浏览器也能把这个消息发送给剪贴簿所有者。应付多个剪贴簿浏览器并非不可能(假定wParam标识特定的浏览器),但剪贴簿所有者处理起来也不容易。
  
WM_PAINTCLIPBOARD这个消息通知剪贴簿所有者修改剪贴簿浏览器的显示区域。同时,wParam是剪贴簿浏览器窗口的句柄,lParam是指向PAINTSTRUCT结构的整体指针。剪贴簿所有者可以从此结构的hdc栏中得到剪贴簿浏览器设备内容的句柄。
  
WM_HSCROLLCLIPBOARD和WM_VSCROLLCLIPBOARD这两个消息通知剪贴簿所有者,使用者已经卷动了剪贴簿浏览器的卷动列。wParam参数是剪贴簿浏览器窗口的句柄,lParam的低字组是卷动请求,并且,如果低字组是SB_THUMBPOSITION,那么lParam的高字组就是滑块位置。
  
处理这些消息比较麻烦,看来并不值得这样做。但是,这种处理对使用者来说是有益的。当从文书处理程序把文字复制到剪贴簿时,使用者在剪贴簿浏览器的显示区域中看见文字还保持着格式时心里会舒坦些。

使用私有剪贴簿数据格式的第三种方法是注册自己的剪贴簿格式名。您向Windows提供格式名,Windows给程序提供一个序号,它可以用作SetClipboardData和GetClipboardData的格式参数。一般来说,采用这种方法的程序也要以一种标准格式把数据复制到剪贴簿。这种方法允许剪贴簿浏览器在它的显示区域中显示数据(没有与CF_OWNERDISPLAY相关的冲突),并且允许其它程序从剪贴簿上复制数据。

例如,假定我们已经编写了一个以位图格式、metafile格式和自己的已注册的剪贴簿格式把数据复制到剪贴簿中的向量绘图程序。剪贴簿浏览器将显示metafile或者位图,其它从剪贴簿上读取位图和metafile的程序将获得这几种格式。但是,当我们的向量绘图程序需要从剪贴簿上读数据时,它会按照自己已注册的格式复制数据,这是因为这种格式可能包含着比位图文件或者metafile更多的信息。

程序透过下面的呼叫来注册一个新的剪贴簿格式:

iFormat = RegisterClipboardFormat (szFormatName) ;
        
iFormat的值介于0xC000和0xFFFF之间。剪贴簿浏览器(或一个通过呼叫EnumClipboardFormats取得目前所有剪贴簿数据格式的程序)可以取得这种数据格式的ASCII名称,这是通过下面呼叫实作的:

GetClipboardFormatName (iFormat, psBuffer, iMaxCount) ;
        
Windows将多达iMaxCount个字符复制到psBuffer中。

使用这种方法把数据复制到剪贴簿中的程序写作者,可能需要公开数据格式名称和实际的数据格式。如果这个程序流行起来,那么其它程序就会以这种格式从剪贴簿中复制数据。

实作剪贴簿浏览器


监视剪贴簿内容变化的程序称为「剪贴簿浏览器」。您可以在Windows中得到一个剪贴簿浏览器,但是您也可以编写自己的剪贴簿浏览器程序。剪贴簿浏览器通过传递到浏览器窗口消息处理程序的消息来监视剪贴簿内容的变化。

剪贴簿浏览器链


任意数量的剪贴簿浏览器应用程序都可以同时在Windows下执行,它们都可以监视剪贴簿内容的变化。但是,从Windows的角度来看,只存在一个剪贴簿浏览器,我们称之为「目前剪贴簿浏览器」。Windows只保留一个识别目前剪贴簿浏览器的窗口句柄,并且当剪贴簿的内容发生变化时只把消息发送到那个窗口中。

剪贴簿浏览器应用程序有必要加入「剪贴簿浏览器链」,以便执行的所有剪贴簿浏览器都可以收到Windows发送给目前剪贴簿浏览器的消息。当一个程序将自己注册为一个剪贴簿浏览器时,它就成为目前的剪贴簿浏览器。Windows把先前的目前浏览器窗口句柄交给这个程序,并且此程序将储存这个句柄。当此程序收到一个剪贴簿浏览器消息时,它把这个消息发送给剪贴簿链中下一个程序的窗口消息处理程序。

剪贴簿浏览器的函数和消息


程序透过呼叫SetClipboardViewer函数可以成为剪贴簿浏览器链的一部分。如果程序的主要作用是作为剪贴簿浏览器,那么这个程序在WM_CREATE消息处理期间可以呼叫这个函数,该函数传回前一个目前剪贴簿浏览器的窗口句柄。程序应该把这个句柄储存在静态变量中:

static HWND hwndNextViewer ;
        
//其它行程序
        
case        WM_CREATE :
        
    //其它行程序
        
           hwndNextViewer = SetClipboardViewer (hwnd) ;
        
如果在Windows的一次执行期间,您的程序成为剪贴簿浏览器的第一个程序,那么hwndNextViewer将为NULL。

不管剪贴簿中的内容怎样变化,Windows都将把WM_DRAWCLIPBOARD消息发送给目前的剪贴簿浏览器(最近注册为剪贴簿浏览器的窗口)。剪贴簿浏览器链中的每个程序都应该用SendMessage把这个消息发送到下一个剪贴簿浏览器。浏览器链中的最后一个程序(第一个将自己注册为剪贴簿浏览器的窗口)所储存的hwndNextViewer为NULL。如果hwndNextViewer为NULL,那么程序只简单地将控件权还给系统而已,而不向其它程序发送任何消息(不要把WM_DRAWCLIPBOARD消息和WM_PAINTCLIPBOARD消息混淆了。WM_PAINTCLIPBOARD是由剪贴簿浏览器发送给使用CF_OWNERDISPLAY剪贴簿数据格式的程序,而WM_ DRAWCLIPBOARD消息是由Windows发往目前剪贴簿浏览器的)。

处理WM_DRAWCLIPBOARD消息的最简单方法是将消息发送给下一个剪贴簿浏览器(除非hwndNextViewer为NULL),并使窗口的显示区域无效:

case        WM_DRAWCLIPBOARD :
        
           if     (      hwndNextViewer)
        
                          SendMessage (hwndNextViewer, message, wParam, lParam) ;
        
           InvalidateRect (hwnd, NULL, TRUE) ;
        
           return 0 ;
        
在处理WM_PAINT消息处理期间,通过使用常规的OpenClipboard、GetClipboardData和CloseClipboard呼叫可以读取剪贴簿的内容。

当某个程序想从剪贴簿浏览器链中删除它自己时,它必须呼叫ChangeClipboardChain。这个函数接收脱离浏览器链的程序之窗口句柄,和下一个剪贴簿浏览器的窗口句柄:

ChangeClipboardChain (hwnd, hwndNextViewer) ;
        
当程序呼叫ChangeClipboardChain时,Windows发送WM_CHANGECBCHAIN消息给目前的剪贴簿浏览器。wParam参数是从链中移除它自己的那个浏览器窗口句柄(ChangeClipboardChain的第一个参数),lParam是从链中移除自己后的下一个剪贴簿浏览器的窗口句柄(ChangeClipboardChain的第二个参数)。

当程序接收到WM_CHANGECBCHAIN消息时,必须检查wParam是否等于已经储存的hwndNextViewer的值。如果是这样,程序必须设定hwndNextViewer为lParam。这项工作保证将来的WM_DRAWCLIPBOARD消息不会发送给从剪贴簿浏览器链中删除了自己的窗口。如果wParam不等于hwndNextViewer ,并且hwndNextViewer不为NULL,则把消息送到下一个剪贴簿浏览器。

case        WM_CHANGECBCHAIN :
        
           if ((HWND) wParam == hwndNextViewer)
        
                  hwndNextViewer = (HWND) lParam ;
        
           else if (hwndNextViewer)
        
                                 SendMessage (hwndNextViewer, message, wParam, lParam) ;
        
           return 0 ;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -