本节我们讲述剪贴板的简单实现方法。
本节必须掌握的知识点:
剪贴板数据的标准格式
内存分配
把文本传到剪贴板
从剪贴板中取得文本
打开和关闭剪贴板
第76练:剪贴板的简单用法
12.1.1 剪贴板数据的标准格式
Windows支持各种预定义的剪贴板格式,这些格式在WINUSER.H中定义并有前缀为 CF的标识符。
| 分类 |
标准格式 |
说明 |
| 文本格式 |
CF_TEXT |
以NULL结尾的ANSI字符集,每行结尾含有回车换行符,最后的NULL表示整个数据的结束。 |
| CF_OEMTEXT |
以NULL结尾的OEM字符集,供MS-DOS下的剪贴板使用 |
|
| CF_UNICODETEXT |
类似CF_TEXT,每行以回车换行结束,字符两个NULL标志着整个数据的结束。 |
|
| CF_SYLK |
含有微软件符号链接(Symbolic Link)格式数据的内存块。用与Excel程序交换,是一种ASCII格式,每行以回车换行符结束,但不一定以NULL结尾,现在很少用到。 |
|
| 位图相关 |
CF_BITMAP |
设备相关位图 |
| CF_DIB |
设备无关位图的内存块(以位图信息结构开头,接着有可能是颜色表和位图的位) |
|
| CF_PALETTE |
指向调色板的句柄,通常与CF_DIB一起使用 |
|
| CF_TIFF |
标签图像文件格式的内存块 |
|
| 图元 文件 格式 |
CF_METAFILEPICT |
基于Windows过去支持的图元文件 |
| CF_ENHMETAFILE |
指向32位Windows版本支持的增强型图元文件 |
|
| 其他 格式 |
CF_PENDATA |
和Windows画笔扩展一起使用 |
| CF_WAVE |
声音波形文件 |
|
| CF_RIFF |
资源交换文件格式的多媒体数据 |
|
| CF_HDROP |
和拖放服务一起使用的文件列表 |
■首先,在剪贴板中可以存储三种文本数据类型,相应的有一种相关的剪贴板格式。
● CF_TEXT 一种以NULL结尾的ANSI字符集字符串,字符串的每行结尾处含有一个回车换行符。这是最简单的剪贴板数据格式。要被传输到剪贴板的数据存储在内存块中,传输时使用指向此内存块的句柄。该内存块会成为剪贴板的属性,因此创建这个内存块的程序不应该继续使用它。
● CF_OEMTEXT包含文本数据(与CF_TEXT类似)但使用OEM字符集的内存块。 Windows程序通常不用担心这点;在窗口中运行MS-DOS程序时如果使用剪贴板则需要用到这种类型。
● CF_UNICODETEXT包含Unicode文本的内存块。类似于CF_TEXT,每行以回车换符结束,字符NULL(两个零字节)标志着整个数据的结束。 CF_UNICODETEXT 只在 WindowsNT 下支持。
● CF_LOCALE指向区域设置标识符的句柄,该标识符标明了与剪贴板相关的区域设置。
■剪贴板和 Unicode
只需在调用SetClipboardData和GetClipboardData时加上自己想要的文本格式,Windows便会在剪贴板里处理所有的文本转换。例如,在Windows NT下,如果某程序用CF_TEXT剪贴板数据类型来使用SetClipboardData,那么你也可以用 CF_OEMTEXT来调用GetClipboardData。同理,剪贴板也能把CF_OEMTEXT转换成 CF_TEXT。
在 Windows NT 下,CF_UNICODETEXT、CF_TEXT 和 CF_OEMTEXT 之间可以互相转换。程序调用SetClipboardData时应该用它最方便使用的格式。同理,程序调用 GetClipboardData时也应该用它合适使用的文本格式。如果定义了 UNICODE标识符, 则用CF_UNICODETEXT;如果没有,就用CF_TEXT。
■此外还有两种剪贴板格式,它们在概念上和CF_TEXT格式类似(就是说,它们是基于文本的),但是它们不一定以NULL结尾,因为格式本身定义了数据的结尾。这些格式现在很少用到。
● CF_SYLK含有微软符号链接(Symbolic Link)格式数据的内存块。这种格式被用来在微软的Multiplan、Chart和Excel程序之间交换数据。它是一种ASCII格式,每行以回车换行符结束。
● CF_DIF含有数据交换格式(DIF)数据的内存块。这是一种由Software Arts设计,用来把数据传输到VisiCalc电子表格程序的格式。它也是一种ASCII格式,每行以回车换行符结束。
■与位图相关的剪贴板格式有三种。位图即对应于输出设备像素的矩形位数组。位图和 这些位图剪贴板格式将在第十四章和第十五章详细讨论。
● CF_BITMAP设备相关位图。此位图由位图句柄传输到剪贴板中。再强调一遍,程序把位图传给剪贴板之后不应继续使用此位图。
● CF_DIB定义了设备无关位图的内存块。设备无关位图在第十五章会给出描述。该内存块以位图信息结构开头,接着有可能是颜色表和位图的位。
● CF_PALETTE指向调色板的句柄。它通常和CF_DIB一起使用,用来定义在设备无关位图中使用的调色板。
■剪贴板里的位图数据也可以采用行业标准的TIFF格式存储。
● CF_TIFF包含标签图像文件格式(Tag Image File Format, TIFF)数据的内存块。这是由微软、Aldus和惠普公司联合一些硬件制造商共同设计的格式。这种格式从惠普网站上可以找到。
■还有两种图元文件格式,我们会在第十七章详细描述。图元文件是以二进制形式存储的绘图命令的集合。
● CF_METAFILEPICT基于Windows过去支持的图元文件的“图元文件图片”。
● CF_ENHMETAFILE指向32位Windows版本支持的增强型图元文件的句柄。
■最后还有一些其他不同的剪贴板格式:
● CF_PENDATA和Windows画笔扩展一起使用。
● CF_WAVE声音(波形)文件。
● CF_RIFF资源交换文件格式(RIFF)的多媒体数据。
● CF_HDROP和拖放服务一起使用的文件列表。
12.1.2 内存分配
■全局内存块的创建
当程序把一些数据传到剪贴板时,程序必须分配一个内存块并移交给剪贴板。当我们需要分配内存时,由于剪贴板中存储的内存块必须在Windows下运行的应用程序之间共享,所以malloc函数不足以完成此任务。取而代之的是Windows API函数GlobalAlloc。
●hGlobal = GlobalAlloc (uiFlags, dwSize) ;
这个函数有两个参数:一系列可能的标志和被分配内存块的字节大小。函数返回HGLOBAL 类型的句柄,这个句柄被称为“指向全局内存块的句柄” (handle to a global memory block)或“全局句柄”。这个函数返回值为NULL时就表示没有足够的内存空间可以分配。
尽管GlobalAlloc的两个参数定义不同,但它们都是32位无符号整数。如果把第一个参数设为0,那么使用的就是标志GMEM_FIXED。这种情况下,GlobalAlloc返回的全局句柄实际是一个指向被分配内存块的指针。
如果想让内存块中的每个字节初始化为0,则可以用标志GMEM_ZEROINIT。在 Windows头文件中,简短的GPTR标志把GMEM_FIXED和GMEM_ZEROINIT标志定义在一起:
#define GPTR (GMEM_FIXED I GMEM_ZEROINIT)
●还有一个内存重分配函数:
hGlobal = GlobalReAlloc (hGlobal, dwSize, uiFlags);
如果内存块扩大了,可以用GMEM_ZEROINIT标志把新分配的字节初始化为0。
●取得内存块大小的函数如下:
dwSize = GlobalSize (hGlobal);
●释放内存的函数是:
GlobalFree(hGlobal);
早期的16位Windows版本会尽量避免GMEM_FIXED标志,因为Windows不能在物理内存中移动内存块。在32位Windows中,GMEM_FIXED可以正常使用,因为它返回一个虚拟地址,操作系统可以通过改变页表移动物理内存里的内存块。在16位Windows上编程,推荐在GlobalAlloc中用GMEM_MOVEABLE标志。在Windows头文件中还定义了个缩写标识符,用来把可移动内存初始化为0:
#define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT)
GMEM_MOVEABLE标志使Windows能在虚拟内存中移动内存块。这并不意味着内存块 会在物理内存中移动,仅仅是此应用程序用来读/写内存块的地址可能会改变。
尽管GMEM_MOVEABLE经常用于16位Windows版本,但现如今通常都不再使用了。 虽然如此,如果应用程序频繁地分配、重分配、释放不同大小的内存块,应用程序虚拟地址空间就会变得七零八碎。你可能会把虚拟内存地址用完。如果这是个潜在的问题,就需要用可移动内存,具体用法如下。
先定义一个指针(例如,指向整数类型的指针)和一个GLOBALHANDLE类型的变量:
int p;
GLOBALHANDLE hGlobal;
然后分配内存,例如:
hGlobal = GlobalAlloc (GHND, 1024);
●和其他Windows句柄一样,不要太在意具体数字代表什么。只要存储下来就可以了。 如果需要访问这个内存块,可调用以下函数把句柄转换为指针:
p = (int *) GlobalLock (hGlobal);
●在内存块被锁定期间,Windows会修复虚拟内存地址。内存块不会移动。访问完内存块之后,调用以下函数使Windows自由移动虚拟内存中的内存块:
GlobalUnlock (hGlobal);
为确保这个过程正确(并且体验早期Windows程序员所经历过的苦痛)应该在单个消息过程中锁定和解锁内存块。
●如果想释放内存,则调用GlobalFree,传入句柄而不是指针。如果现在没有可访问的句柄,那么用以下函数即可:
hGlobal = GlobalHandle (p) ;
可以在释放一个内存块之前多次锁定它。Windows有一个锁计数器,内存块被自由移动前,每次锁定都要有一个对应的解锁。Windows在虚拟内存中移动内存块不需要把字节从一个位置复制到另一个位置——它只需要操作页表。一般来说,在32位版本的Windows 上,为应用程序分配可移动内存块的唯一原因就是防止虚拟内存变得零碎。用剪贴板时,也应该用可移动内存。
为剪贴板分配内存时,应该用GlobalAlloc函数,并同时使用GMEM_MOVEABLE和 GMEM_SHARE标志作为参数。GMEM_SHARE标志使内存块能被其他Windows应用程序共享。
■其它标准内存管理函数
●内存重分配函数
hGlobal = GlobalReAlloc(hGlobal,dwSize,uiFlags);
●获取内存块大小
dwSize = GlobalSize(hGlobal);
●获取内存块句柄
hGlobal = GlobalHandle(p); //p为指向内存块的地址(指可移动内存块)
●释放内存函数
GlobalFree(hGlobal);
12.1.3 把文本传到剪贴板
假设你想把一个ANSI字符串传到剪贴板。你有一个指向这个字符串的指针(称作 pString),并且想传个字符,它们也可能不是以NULL终止。
■把文本复制到剪贴板的步骤
●首先得用GlobalAlloc来分配足够容纳这个字符串的内存块,并给终止符NULL留下空间:
hGlobal = GlobalAlloc (GHND | GMEM_SHARE, iLength + 1) ;
●如果内存块分配失败,GlobalAlloc的值为NULL。如果分配成功,则锁定这个内存块并得到指向它的指针:
pGlobal = GlobalLock (hGlobal);
●把字符串复制到全局内存块:
for (i = 0 ; i < wLength ; i++)
*pGlobal++ = *pString++ ;
不需要加终止符NULL,因为GlobalAlloc的GHND标志在分配内存时把整个内存块置0。
●解锁内存:
GlobalUnlock(hGlobal);

最低0.47元/天 解锁文章
1081

被折叠的 条评论
为什么被折叠?



