12、剪贴板

1、内存分配

使用剪贴板时,程序必须申请一个内存块用于存储数据,并把内存块交给剪贴板。

申请内存块可以使用GlobalAlloc函数,GlobalAlloc的返回值是一个HGLOBAL类型的句柄。如果GlobalAlloc调用时指定了GMEM_FIXED标志位,内存块地址是固定不变的,句柄值实际是一个内存块指针;如果指定了GMEM_MOVEABLE标志位,那么windows会在需要情况下移动该内存块(虚拟内存),以防止内存碎片化。由于内存块可以移动,返回值自然不是固定的内存块指针,而就是一个句柄值。使用可移动的内存块前必须使用GlobalLock锁定内存块,防止在访问期间windows移动内存块。GlobalLock会根据内存块句柄返回对应的内存指针,访问完内存块后,使用GlobalUnlock解锁内存块。

windows为内存块维护一个锁计数器。GlobalLock时计数值加1,GlobalUnlock时计数值减1。必须调用相同次数的GlobalUnlock,使计数值为0,才能成功解锁内存块。

GlobalAlloc用于分配全局内存,而LocalAlloc用于分配局部内存。实际上,全局堆和局部堆是16位系统的概念,对于现在的32位或者64位系统的程序,都只有一个默认堆。所以GlobalAllocLocalAlloc的效果实际是一样的,MSDN上说应该尽量使用HeapAlloc系列的函数。

GlobalAlloc相关的其他函数:

  • GlobalHandle:根据内存指针获取内存块句柄
  • GlobalFree:释放内存块
2、复制数据到剪贴板

分配好内存并写入数据后,需要把数据传入剪贴板:

  • OpenClipboard:打开剪贴板
  • EmptyClipboard:清空剪贴板
  • SetClipboardData:把内存句柄传给剪贴板
  • CloseClipboard:关闭剪贴板

调用SetClipBoardData时需要指定数据的格式,windows预定义了很多标准的剪贴板数据格式,其标识符带有CF_前缀。

在使用剪贴板过程中需要注意:

  • 只有一个程序能够打开剪贴板,所以OpenClipBoard后必须尽快CloseClipBoard
  • 不能把一个锁定的内存块句柄传给剪贴板
  • SetClipBoardData后,内存块已经归系统所有,不能对内存块进行写入和释放操作。但在CloseClipBoard之前,可以利用SetClipBoardData返回的句柄,对内存块进行锁定和读取操作。在CloseClipBoard之前必须解锁内存块。
3、从剪贴板粘贴数据
  • IsClipboardFormatAvailable:判断剪贴板数据内容格式,可以不打开剪贴板
  • OpenClipboard:打开剪贴板
  • GetClipboardData:获取数据的全局内存块句柄
  • GlobalLock:锁定内存块
  • 把数据拷贝到程序中
  • GlobalUnlock:解锁内存块
  • CloseClipboard:关闭剪贴板
4、剪贴板和字符编码

windows为剪贴板数据定义了多种文本格式,比如CF_TEXTCF_OEMTEXTCF_UNICODETEXT。复制、粘贴时可以使用不同的字符编码,GetClipboardData内部会完成文本转换。比如,程序A文本使用ANSI编码,程序B文本使用UNICODE编码。从程序A复制文本,调用SetClipboardData自然使用CF_TEXT格式。在程序B粘贴文本,调用GetClipboardData使用CF_UNICODETEXT标识符,获取到的文本就是UNICODE格式。

5、同时使用多种数据格式

在调用OpenClipboardCloseClipboard之间,可以多次调用SetClipboardData,把多个不同格式的数据写入剪贴板。但是,对一个格式不能多次写入数据。

OpenClipboard(hwnd);
EmptyClipboard();
SetClipboardData(CF_TEXT, hGlobalText);
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();

粘贴数据时,调用GetClipboardData,传入不同格式就可以获取到不同的数据:

hGlobalText = GetClipboardData(CF_TEXT);
hBitmap = GetClipboardData(hBitmap);

相关函数:

  • IsClipboardFormatAvailable:判断剪贴板中是否包含某种格式数据
  • EnumClipboardFormats:枚举剪贴板中包含的数据格式
    第一次调用时,把传参设为0,EnumClipboardFormats会返回剪贴板中第一个数据的格式。后续的调用传入前一次调用的返回结果,EnumClipboardFormats返回剪贴板中下一个数据的格式。当传参为剪贴板中最后一个数据的格式,EnumClipboardFormats返回0,枚举结束。
  • CountClipboardFormats:获取剪贴板中数据格式的数量。
6、写时复制

按照前面的方法,复制数据时就需要为数据分配好内存。但是,这些内存可能并没有被实际使用,也就是没有粘贴动作,或是过了很长时间才会被使用。这会造成内存浪费,数据量大时尤其严重。

为了解决这个问题,可以把内存分配动作延迟到粘贴动作发生时。

调用SetClipboardData时,把第二传参句柄值设为NULL。那么,当调用GetClipboardData时,windows检查数据句柄,如果为NULL,就向 剪贴板所有者 发送消息,请求一个数据句柄。

  • WM_RENDERFORMAT
    GetClipboardData被调用时,剪贴板所有者会收到WM_RENDERFORMAT消息。消息处理中,不需要OpenClipboard,即使调用也是失败,因为GetClipboardData前已经OpenClipboard;需要的是为数据分配内存,并调用SetClipboardData把数据传给剪贴板,其wParam参数指定了需要的数据格式。
  • WM_RENDERALLFORMAT
    剪贴板所有者即将关闭前会收到WM_RENDERALLFORMAT消息。消息处理中,应该为所有需要的数据格式准备好数据,并传给剪贴板。在这之前需要OpenClipboard
  • WM_DESTROYCLIPBOARD
    EmptyClipboard被调用时,剪贴板所有权发生转移,windows发送WM_DESTROYCLIPBOARD消息通知原剪贴板所有者。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值