第十二章 剪贴板
12.1 剪贴板的简单用法
12.1.1 剪贴板数据的标准格式
CF_TEXT 一种以NULL结尾的ANSI字符集字符串,字符串的每行结尾有一个回车换行符。
CF_OEMTEXT 包含文本数据(CF_TEXT)但使用OEM字符集的内存块。 在窗口运行MS-DOS程序时如果使用剪贴板需要使用这种类型
CF_UNICODETEXT 包含Unicode文本的内存块。类似于CF_TEXT, 以回车换行结束,字符NULL(两个0字节)标志整个数据结束。
CF_LOCALE 指向区域设置标识符的句柄,表明了与剪贴板相关的区域设置
CF_SYLK 包含微软符号链接格式数据的内存块。用作multiplan, chart Excel程序之间交换数据。 一种ASCII格式,每行以回车换行符结束。
CF_DIF 含有数据交换格式DIF数据的内存块。
与位图相关的剪贴板格式有三种。即位图对应于输出设备像素的矩形位数组。
CF_BITMAP 设备相关位图。 (程序把位图传给剪贴板之后不应该继续使用此位图)
CF_DIB 定义了设备无关位图的内存块。
CF_PALETTE 指向调色板的句柄。 通常和CF_DIB一起使用,用来定义设备相关位图中使用的调色板。
CF_TIFF 包含标签图像文件格式(Tag Image File Format)数据的内存块。
还有两种图元格式(以二进制形式存储的绘图命令的集合)
CF_METAFILEPICT 基于windows过去支持的图元文件的“图元文件图片”。
CF_ENHMETAFILE 指向32位windows版本支持的增强型图元文件的句柄
CF_PENDATA 和windows画笔一起使用
CF_WAVE 声音波形文件
CF_RIFF 资源交换文件格式(RIFF)的多媒体数据
CF_HDROP 和拖放服务一起使用的文件列表。
12.1.2 内存分配
由于剪贴板中存储的内存块必须在windows下运行的应用程序之间共享,malloc函数不足以完成此任务。
取而代之的是,windows早起设计的内存分配函数,
hGlobal = GlobalAlloc(uiFlags, dwSize);
返回HGLOBAL类型的句柄,称为 指向全局内存块的句柄(handle to a global memory block) "全局句柄"
返回NULL表示没有足够的内存空间可以分配。
uiFlags 0 使用GME_FIXED 函数返回的全局句柄实际是一个指向被分配内存块的指针
GME_ZEROINIT 把分配的内存块初始化为0.
#define GPTR (GMEM_FIXED | GMEM_ZEROINIT)
内存重分配函数
hGlobal = GlobalReAlloc(hGlobal, dwSize, uiFlags);
取得内存分配大小
dwSize = GlobalSize(hGlobal);
释放内存的函数是
GlobalFree(hGlobal);
#define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT)
GMEM_MOVEABLE 使得windows能在虚拟内存中移动内存块。 并不意味内存块绘制物理内存中移动,仅仅是此应用程序用来读写内存块的地址可能会改变。
GMEM_MOVEABLE 仅用16位系统上。
如果应用程序频繁分配,重分配,释放不同大小的内存块,应用程序虚拟机制控件会变得七零八碎。可能会把虚拟内存地址用完。如果是个潜在问题,就需要使用可移动内存。
做法如下
定义一个指针
int * p;
GLOBALHANDLE hGlobal;
分配内存
hGlobal = GlobalAlloc(GHND, 1024);
p = (int *) GlobalLock(hGlobal); //把全局内存句柄转换为指针, windows会修复内虚拟内存地址。内存块不会移动。
使用完以后调用
GlobalUnlock(hGlobal); 解锁内存块
释放内存 GlobalFree(hGlobal); //参数是全局内存句柄而非指针
如果没有可访问的句柄可以使用
hGlobal = GlobalHandle(p);
使用可移动内存的原因是防止内存碎片,在使用剪贴板时也可以使用可移动内存。
为剪贴板分配内存使用 GlobalAlloc 标志使用 GMEM_MOVEABLE 和 GMEM_SHARE标志作为参数。GMEM_SHARE标志使得内存能被其他应用程序共享
12.1.3 把文本传到剪贴板
hGlobal = GlobalAlloc(GHND | GMEM_SHARE, iLength + 1); //给字符串终止符预留空间
如果分配失败 hGlobal = NULL
如果分配成功,锁定此空间
pGlobal = GlobalLock(hGlobal);
把字符串复制到内存块
for ( i = 0; i < wLength; i++)
*pGlobal++ = *pString++; //不需要加终止符,因为GHND标志初始化时全部填充0
解锁内存
GlobalUnlock(hGlobal);
现在可以传入剪贴板
OpenClipboard(hwnd);
EmptyClipboard(); 清空剪贴板
SetClipboardData(CF_TEXT, hGlobal); // 把全局句柄传入,标志设置为CF_TEXT
CloseClipboard(); 完事
注意:
在单个消息过程中调用OpenClipboard 和CloseClipboard. 避免不必要长时间打开剪贴板
不要把锁定的内存传给剪贴板 (应该解锁)
调用SetClipboardData 以后不可再使用该内存块,他已经不再属于程序。 如果需要继续使用,应该在复制一份或者从剪贴板中读取。
在SetClipboardData 和 CloseClipboard之间,可以继续应用该内存块,但是不要使用传给SetClipboardData函数的全局句柄。
你可以使用SetClipboardData返回的句柄,并锁定来修改内存。 在调用CloseClipboard之前解锁这个句柄。
12.1.4 从剪贴板中取得文本
判断是否存在文本
bAvailable = IsClipboardFormatAvailable(CF_TEXT);
若存在返回TRUE
OpenClipboard(hwnd) 打开剪贴板
hGlobal = GetClipboardData(CF_TEXT); 获取全局句柄,如果无CF_TEXT 则返回NULL,并关闭剪贴板 CloseClipboard
pText = (char *)malloc(GlobalSize(hGlobal)); 申请内存
pGlobal = GlobalLock(hGlobal); 锁定全局句柄
strcpy(pText, pGlobal); 拷贝数据
或者 while( *pText++ = *pGlobal++)
;
GlobalUnlock(pGlobal); 解锁全局句柄
CloseClipboard 关