本节我们讲述剪贴板的简单实现方法。
本节必须掌握的知识点:
剪贴板数据的标准格式
内存分配
把文本传到剪贴板
从剪贴板中取得文本
打开和关闭剪贴板
第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);
●现在你有了一个全局内存句柄,它指向一块包含以NULL为终止符的文本的内存块。要把这个内存块传入剪贴板,应打开剪贴板并且清空它:
OpenClipboard (hwnd) ;
EmptyClipboard () ;
●通过使用CF_TEXT标识符,把内存句柄传给剪贴板,并且关闭剪贴板:
SetClipboardData(CF_TEXT, hGlobal);
CloseClipboard();
就此完成。
■以下是关于这个过程的一些规则:
●在处理单个消息的过程中调用OpenClipboard和CloseClipboard。避免不必要地长期打开剪贴板。
●不要把一个锁定的内存句柄传给剪贴板。
●调用SetClipboardData后,不要继续使用该内存块。它己不再属于程序,你应该把该句柄当作是无效的。如果需要继续使用数据,应再复制一份或者从剪贴板中读取(下一小节会讲到)。在调用SetClipboardData和CloseClipboard之间,也可以继续引用内存块,但是不要使用传给SetClipboardData函数的全局句柄。你可以使用由这个函数返回的全局句柄。锁定这个句柄来访问内存。在调用 CloseClipboard之前解锁这个句柄。
■示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifdef _WIN32
#include <Windows.h>
#endif
bool CopyTextToClipboard(const char* text) {
bool success = false;
#ifdef _WIN32
// 打开剪贴板
if (OpenClipboard(NULL)) {
// 清空剪贴板内容
if (EmptyClipboard()) {
// 计算文本长度
size_t textLength = strlen(text);
// 分配全局内存来存储文本数据
HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE,
(textLength + 1) * sizeof(char));
if (hGlobalMemory != NULL) {
// 锁定全局内存并获取指向内存区域的指针
char* lpData = (char*)GlobalLock(hGlobalMemory);
if (lpData != NULL) {
// 将文本复制到全局内存中
strcpy(lpData, text);
// 解锁全局内存
GlobalUnlock(hGlobalMemory);
// 设置剪贴板数据
if (SetClipboardData(CF_TEXT, hGlobalMemory) != NULL) {
success = true;
}
}
}
}
// 关闭剪贴板
CloseClipboard();
}
#endif
return success;
}
int main() {
const char* text = "Hello, World!";
if (CopyTextToClipboard(text)) {
printf("文本已复制到剪贴板。\n");
} else {
printf("无法将文本复制到剪贴板。\n");
}
return 0;
}
12.1.4 从剪贴板中取得文本
从剪贴板中取得文本只比把文本传给剪贴板稍稍麻烦一点点。首先要确定剪贴板实际 上是否包含CF_TEXT格式的文本,最简单的方法之一是调用以下函数:
bAvailable = IsClipboardFormatAvailable (CF_TEXT);
如果剪贴板包含CF_TEXT数据,这个函数返回TRUE(非0)。在第九章的POPPAD2程序中,我们用这个函数检査Edit菜单上的Paste项是否可用。IsClipboardFormatAvailable是 剪贴板少数几个不需要打开剪贴板就能使用的函数之一。但是,在之后打开剪貼板以取得文本时,应该再次进行检査(用同样的函数或者其他某种方法),以确定CF_TEXT数据是否仍保存在剪贴板中。
■从剪贴板取得文本步骤
●要把文本传出,先如下打开剪贴板:
OpenClipboard(hwnd);
●取得指向文本的全局内存块的句柄:
hGlobal = GetClipboardData (CF_TEXT);
如果剪贴板没有CF_TEXT格式的数据,此句柄为NULL。这是判断剪贴板是否有文本数据的另一个方法。如果GetClipboardData返回NULL,则应关闭剪贴板,其他什么都不要做。
从GetClipboardData得到的句柄不属于你的程序,它属于剪贴板。这个句柄只在 GetClipboardData和CloseClipboard调用之间有效。你不能释放此句柄或是改变它引用的数据。如果想继续访问数据,应该复制内存块。
●以下是把数据复制到你程序里的方法。分配一个指向和剪贴板数据块一样大小的内存 块的指针:
pText = (char *) malloc (GlobalSize (hGlobal));
●记住,hGlobal是调用GetaipboardData取得的全局句柄。现在锁定该句柄,得到一个指向剪贴板数据块的指针:
pGlobal = GlobalLock (hGlobal);
●现在复制数据:
strcpy(pText, pGlobal);
或者也可以用简单的C代码:
while (*pText++ =*pGlobal++);
●解锁内存块之后关闭剪貼板:
GlobalUnlock (hGlobal);
Closed ipboard ();
现在你有了一个pText指针,它指向你的程序所拥有的文本副本。
■示例
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#ifdef _WIN32
#include <Windows.h>
#endif
char* GetTextFromClipboard() {
char* text = NULL;
#ifdef _WIN32
// 打开剪贴板
if (OpenClipboard(NULL)) {
// 获取剪贴板中的数据句柄
HANDLE hData = GetClipboardData(CF_TEXT);
if (hData != NULL) {
// 锁定数据句柄并获取指向数据的指针
char* pData = (char*)GlobalLock(hData);
if (pData != NULL) {
// 获取文本长度
size_t textLength = strlen(pData);
// 分配内存来存储文本数据
text = (char*)malloc((textLength + 1) * sizeof(char));
if (text != NULL) {
// 复制文本数据到分配的内存中
strcpy(text, pData);
}
// 解锁数据句柄
GlobalUnlock(hData);
}
}
// 关闭剪贴板
CloseClipboard();
}
#endif
return text;
}
int main() {
char* text = GetTextFromClipboard();
if (text != NULL) {
printf("剪贴板中的文本是:%s\n", text);
free(text);
} else {
printf("无法获取剪贴板中的文本。\n");
}
return 0;
}
12.1.5 打开和关闭剪贴板
无论何时都只有一个程序可以打开剪贴板。调用的目的是当某个程序正在使用剪贴板时,防止剪贴板内容改变。会返回一个表示剪贴板是否已被成功打开的BOOL值。如果有另一个应用程序尚未关闭剪贴板,便无法打开它。如果每个程序都根据用户命令尽快打开和关闭剪贴板,你可能就不会遇到无法打开剪贴板这个问题。
如果程序之间不那么谦让或者碰到抢占式多任务,可能就会有些麻烦。即使你的程序在把数据放到剪贴板和用户调用粘贴项之间这段时间里没有丢失输入焦点,也不要假设你传入的数据还在剪贴板里。后台进程在这段时间里有可能己经访问过剪贴板。
还要当心涉及消息框时的一个更微妙的问题:在不能分配足够的内存把数据复制到剪 贴板时,你或许想显示一个消息框。但是,如果这个消息框不是系统模态,那么用户可以在消息框显示时切换到另一个应用程序。所以,要么把消息框变成系统模态,要么在显示消息框前关闭剪贴板。
如果在打开剪贴板的同时又显示了对话框,也可能会碰到问题。对话框的编辑字段会使用剪贴板来剪切和粘贴文本。
■示例
#include <stdio.h>
#include <stdbool.h>
#ifdef _WIN32
#include <Windows.h>
#endif
bool OpenClipboard() {
bool success = false;
#ifdef _WIN32
success = (bool)OpenClipboard(NULL);
#endif
return success;
}
bool CloseClipboard() {
bool success = false;
#ifdef _WIN32
success = (bool)CloseClipboard();
#endif
return success;
}
int main() {
if (OpenClipboard()) {
printf("剪贴板已打开。\n");
// 在此处进行剪贴板操作
if (CloseClipboard()) {
printf("剪贴板已关闭。\n");
} else {
printf("剪贴板关闭失败。\n");
}
} else {
printf("无法打开剪贴板。\n");
}
return 0;
}
12.1.6 第76练:剪贴板的简单用法
/*------------------------------------------------------------------------
076 WIN32 API 每日一练
第76个例子CLIPTEXT.C:剪贴板的简单用法
EnableMenuItem 函数
IsClipboardFormatAvailable函数
OpenClipboard函数
GetClipboardData函数
GlobalSize函数
GlobalLock函数
CloseClipboard函数
GlobalAlloc函数
GlobalUnlock函数
EmptyClipboard函数
SetClipboardData函数
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);
//条件编译
#ifdef UNICODE
#define CF_TCHAR CF_UNICODETEXT //重定义剪贴板的数据格式
TCHAR szDefaultText[] = TEXT("Default Text - Unicode Version");
TCHAR szCaption[] = TEXT("Clipboard Text Transfers - Unicode Version");
#else
#define CF_TCHAR CF_TEXT
TCHAR szDefaultText[] = TEXT("Default Text - ANSI Version");
TCHAR szCaption[] = TEXT("Clipboard Text Transfers - ANSI Version");
#endif
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
static TCHAR szAppName[] = TEXT("ClipText");
…(略)
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
static PTSTR pText;
BOOL bEnable;
HGLOBAL hGlobal;
HDC hdc;
PTSTR pGlobal;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
//发送一个WM_COMMAND消息,处理IDM_EDIT_RESET
SendMessage(hwnd,WM_COMMAND,IDM_EDIT_RESET,0);
return 0;
case WM_INITMENUPOPUP://根据剪贴板是否有内容,显示相应的菜单
EnableMenuItem((HMENU)wparam,IDM_EDIT_PASTE,
IsClipboardFormatAvailable(CF_TCHAR ?
MF_ENABLED :MF_GRAYED));//确定剪贴板是否包含指定格式的数据
bEnable = pText ? MF_ENABLED : MF_GRAYED;
EnableMenuItem((HMENU)wparam,IDM_EDIT_CUT,bEnable);
EnableMenuItem((HMENU)wparam,IDM_EDIT_COPY,bEnable);
EnableMenuItem((HMENU)wparam,IDM_EDIT_CLEAR,bEnable);
break;
case WM_COMMAND://菜单消息处理
switch (LOWORD(wparam))
{
case IDM_EDIT_PASTE:
OpenClipboard(hwnd);//打开剪贴板
if (hGlobal = GetClipboardData(CF_TCHAR))//获取剪贴板数据
{
/*Windows系统内存管理器为了避免内存的碎片化,会通过调整页表的方式移动内存块,页表重新映射物理地址。锁定期间,Windows会修复虚拟内存地址,不会移动内存块,防止虚拟地址被改变。此时就可以得到一个确定的本进程所属的指针指向该内存块,其他进程的指针不可以访问。*/
//锁定内存块,获取指向剪贴板数据的指针
pGlobal = GlobalLock(hGlobal);
if (pText)
{
free(pText);//清空
pText = NULL;
}
pText = malloc(GlobalSize(hGlobal));
lstrcpy(pText, pGlobal);
/*当使用剪切板时,因为剪贴板的内存块是共享的,多个程序同时使用剪贴板时可能会发生冲突。锁定期间,表示剪贴板为私有,使用完后,应及时释放剪贴板*/
GlobalUnlock(hGlobal);//解锁内存块
InvalidateRect(hwnd, NULL, TRUE);
}
CloseClipboard();//关闭剪贴板
return 0;
case IDM_EDIT_CUT:
case IDM_EDIT_COPY:
if (!pText) //为空退出
return 0;
//分配可移动的初始化为0的内存并拷贝数据到全局内存块里
//GHND默认堆内分配可移动内存
hGlobal = GlobalAlloc(GHND |GMEM_SHARE,
(lstrlen (pText + 1)) *sizeof(TCHAR));
pGlobal = GlobalLock(hGlobal);
lstrcpy(pGlobal,pText);//复制
GlobalUnlock(hGlobal);//解锁内存块
//将内存块传入剪贴板
OpenClipboard(hwnd);//打开剪贴板
EmptyClipboard();//清空
//将内存块复制给保存剪贴板数据的全局句柄
SetClipboardData(CF_TCHAR,hGlobal);
CloseClipboard();//关闭剪贴板
case IDM_EDIT_CLEAR:
if (pText)
{
free(pText);
pText = NULL;
}
InvalidateRect(hwnd,NULL,TRUE);
return 0;
case IDM_EDIT_RESET:
if (pText)
{
free(pText);//清空
pText = NULL;
}
pText = malloc((lstrlen(szDefaultText) + 1) * sizeof(TCHAR));
lstrcpy(pText,szDefaultText);
InvalidateRect(hwnd,NULL,TRUE);
return 0;
}
case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);
GetClientRect(hwnd,&rect);
if (pText != NULL)//绘制文本
DrawText(hdc,pText,-1,&rect,DT_EXPANDTABS //扩展制表符
| DT_WORDBREAK);//断词
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
if(pText)
free(pText);//释放内存块
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,message,wparam,lparam);
}
/******************************************************************************
EnableMenuItem 函数:启用,禁用或显示指定的菜单项
BOOL EnableMenuItem(
HMENU hMenu, //菜单句柄
UINT uIDEnableItem, //指定菜单栏,菜单或子菜单中的项目
UINT uEnable //启用,禁用还是灰色
);
*******************************************************************************
IsClipboardFormatAvailable函数:确定剪贴板是否包含指定格式的数据
BOOL IsClipboardFormatAvailable(
UINT format
);
*******************************************************************************
OpenClipboard函数:开剪贴板进行检查,并阻止其他应用程序修改剪贴板内容。
BOOL OpenClipboard(
HWND hWndNewOwner//与打开的剪贴板关联的窗口的句柄。如果此参数为NULL,则打开的剪贴板与当前任务关联。
);
*******************************************************************************
GetClipboardData函数:以指定的格式从剪贴板检索数据。剪贴板必须先前已打开。
HANDLE GetClipboardData(
UINT uFormat//剪贴板格式
);
*******************************************************************************
GlobalSize函数:检索指定的全局内存对象的当前大小,以字节为单位
SIZE_T GlobalSize(
HGLOBAL hMem//全局内存对象的句柄
);
*******************************************************************************
GlobalLock函数:锁定全局内存对象,并返回一个指向该对象内存块的第一个字节的指针
LPVOID GlobalLock(
HGLOBAL hMem//全局内存对象的句柄
);
*******************************************************************************
CloseClipboard函数:关闭剪贴板
BOOL CloseClipboard();
*******************************************************************************
GlobalAlloc函数:从堆中分配指定的字节数
DECLSPEC_ALLOCATOR HGLOBAL GlobalAlloc(
UINT uFlags,//内存分配属性。如果指定零,则默认值为GMEM_FIXED。
SIZE_T dwBytes//字节数
);
*******************************************************************************
GlobalUnlock函数:减少与GMEM_MOVEABLE分配的内存对象关联的锁计数。该函数对使用GMEM_FIXED分配的内存对象无效。
BOOL GlobalUnlock(
HGLOBAL hMem//全局内存对象的句柄
);
*******************************************************************************
EmptyClipboard函数:清空剪贴板并释放对剪贴板中数据的句柄。然后,该函数将剪贴板的所有权分配给当前打开剪贴板的窗口。
BOOL EmptyClipboard();
*******************************************************************************
SetClipboardData函数:以指定的剪贴板格式将数据放置在剪贴板上。
HANDLE SetClipboardData(
UINT uFormat,//剪贴板格式
HANDLE hMem//指定格式的数据句柄
);
*/
●资源脚本文件CLIPTEXT.rc包含一个菜单资源和一个快捷键资源:
/
//
// Menu
//
ClipText MENU
BEGIN
POPUP "&EDIT"
BEGIN
MENUITEM "CU&T\tCtrl+X", IDM_EDIT_CUT
MENUITEM "©\tCtrl+C", IDM_EDIT_COPY
MENUITEM "&PASTE\tCtrl+V", IDM_EDIT_PASTE
MENUITEM "DE&LETE\tDEL", IDM_EDIT_CLEAR
MENUITEM SEPARATOR
MENUITEM "&RESET", IDM_EDIT_RESET
END
END
/
//
// Accelerator
//
IDR_ACCELERATOR1 ACCELERATORS
BEGIN
"X", IDM_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
"C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
"V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT
END
●运行结果:
图12-1 剪贴板的简单用法
总结
实例CLIPTEXT.C的主窗口添加了菜单EDIT和快捷键,演示了对剪贴板的操作。
窗口过程:
●WM_CREATE消息:调用SendMessage函数向窗口发送一个WM_COMMAND消息,处理RESET菜单。
●WM_INITMENUPOPUP消息:对菜单项进行初始化工作。调用IsClipboardFormatAvailable函数判断当前剪贴板是否有内容。如果有,则启用PASTE菜单,否则禁用。同时,判断pText缓冲区是否有字符串。如果有,则启用CUT、COPY和CLEAR菜单,如果没有,则禁用。
●WM_COMMAND消息:处理菜单消息。
PASTE粘贴:打开剪贴板获取剪贴板数据,参考12.1.4小节,此处不再赘述。
CUT剪贴和COPY复制:把数据复制到剪贴板,参考12.1.3小节,此处不再赘述。
CLEAR清空:pText = NULL;调用InvalidateRect函数重绘窗口客户区。
RESET重置:清空pText缓冲区,将默认字符串拷贝到pText缓冲区,重绘窗口客户区。
●WM_PAINT消息:调用DrawText将pText缓冲区字符串绘制到窗口客户区。
●WM_DESTROY消息:是否pText缓冲区,销毁窗口。