Windows剪贴板是一种比较简单同时也是开销比较小的IPC(InterProcess Communication,进程间通讯)机制。Windows系统支持剪贴板IPC的基本机制是由系统预留的一块全局共享内存,用来暂存在各进程间进行交换的数据:提供数据的进程创建一个全局内存块,并将要传送的数据移到或复制到该内存块;接受数据的进程(也可以是提供数据的进程本身)获取此内存块的句柄,并完成对该内存块数据的读取。

为使剪贴板的这种IPC机制更加完善和便于使用,需要解决好如下三个问题:提供数据的进程在结束时Windows系统将删除其创建的全局内存块,而接受数据的进程则希望在其退出后剪贴板中的数据仍然存在,可以继续为其他进程所获取;能方便地管理和传送剪贴板数据句柄;能方便设置和确定剪贴板数据格式。为完善上述功能,Windows提供了存在于USER32.dll中的一组API函数、消息和预定义数据格式等,并通过对这些函数、消息的使用来管理在进程间进行的剪贴板数据交换。

Windows系统为剪贴板提供了一组API函数和多种消息,基本可以满足编程的需要。而且Windows还为剪贴板预定义了多种数据格式。通过这些预定义的格式,可以使接收方正确再现数据提供方放置于剪贴板中的数据内容。

文本剪贴板和位图剪贴板的使用

这两种剪贴板是比较常用的。其中,文本剪贴板是包含具有格式CF_TEXT的字符串的剪贴板,是最经常使用的剪贴板之一。在文本剪贴板中传递的数据是不带任何格式信息的ASCII字符。若要将文本传送到剪贴板,可以先分配一个可移动全局内存块,然后将要复制的文本内容写入到此内存区域。最后调用剪贴板函数将数据放置到剪贴板:

DWORD dwLength = 100; // 要复制的字串长度

HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 分配内存

LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 锁定内存

for (int i = 0; i < dwLength; i++) // 将"*"复制到全局内存块

*lpGlobalMemory++ = '*';

GlobalUnlock(hGlobalMemory); // 锁定内存块解锁

HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄

::OpenClipboard(hWnd); // 打开剪贴板

::EmptyClipboard(); // 清空剪贴板

::SetClipboardData(CF_TEXT, hGlobalMemory); // 将内存中的数据放置到剪贴板

::CloseClipboard(); // 关闭剪贴板

这里以OpenClipboard()打开剪贴板,并在调用了EmptyClipboard()后使hWnd指向的窗口成为剪贴板的拥有者,一直持续到CloseClipboard()函数的调用。在此期间,剪贴板为拥有者所独占,其他进程将无法对剪贴板内容进行修改。

从剪贴板获取文本的过程与之类似,首先打开剪贴板并获取剪贴板的数据句柄,如果数据存在就拷贝其数据到程序变量。由于GetClipboardData()获取的数据句柄是属于剪贴板的,因此用户程序必须在调用CloseClipboard()函数之前使用它:

HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄

::OpenClipboard(hWnd); // 打开剪贴板

HANDLE hClipMemory = ::GetClipboardData(CF_TEXT);// 获取剪贴板数据句柄

DWORD dwLength = GlobalSize(hClipMemory); // 返回指定内存区域的当前大小

LPBYTE lpClipMemory = (LPBYTE)GlobalLock(hClipMemory); // 锁定内存

m_sMessage = CString(lpClipMemory); // 保存得到的文本数据

GlobalUnlock(hClipMemory); // 内存解锁

::CloseClipboard(); // 关闭剪贴板

大多数应用程序对图形数据采取的是位图的剪贴板数据格式。位图剪贴板的使用与文本剪贴板的使用是类似的,只是数据格式要指明为CF_BITMAP,而且在使用SetClipboardData()或GetClipboardData()函数时交给剪贴板或从剪贴板返回的是设备相关位图句柄。下面这段示例代码将把存在于剪贴板中的位图数据显示到程序的客户区:

HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄

::OpenClipboard(hWnd); // 打开剪贴板

HANDLE hBitmap = ::GetClipboardData(CF_BITMAP); // 获取剪贴板数据句柄

HDC hDC = ::GetDC(hWnd); // 获取设备环境句柄

HDC hdcMem = CreateCompatibleDC(hDC); // 创建与设备相关的内存环境

SelectObject(hdcMem, hBitmap); // 选择对象

SetMapMode(hdcMem, GetMapMode(hDC)); // 设置映射模式

BITMAP bm; // 得到位图对象

GetObject(hBitmap, sizeof(BITMAP), &bm);

BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); //位图复制

::ReleaseDC(hWnd, hDC); // 释放设备环境句柄

DeleteDC(hdcMem); // 删除内存环境

::CloseClipboard(); // 关闭剪贴板

多数据项和延迟提交技术

要把数据放入剪贴板,在打开剪贴板后一定要调用EmptyClipboard()函数清除当前剪贴板中的内容,而不可以在原有数据项基础上追加新的数据项。但是,可以在EmptyClipboard()和CloseClipboard()调用之间多次调用SetClipboardData()函数来放置多个不同格式的数据项。例如:

OpenClipboard(hWnd);

EmptyClipboardData();

SetClipboardData(CF_TEXT, hGMemText);

SetClipboardData(CF_BITMAP, hBitmap);

CloseClipboard();

这时如果用CF_TEXT或CF_BITMAP等格式标记去调用IsClipboardFormatAvailable()都将返回TRUE,表明这几种格式的数据同时存在于剪贴板中。以不同的格式标记去调用GetClipboardData()函数可以得到相应的数据句柄。

对于多数据项的剪贴板数据,还可以用CountClipboardFormats()和EnumClipboardFormats()函数得到当前剪贴板中存在的数据格式数目和具体的数据格式。EnumClipboardFormats()的函数原型为:

UINT EnumClipboardFormats(UINT format);

参数format指定了剪贴板的数据格式。如果成功执行将返回format指定的格式的下一个数据格式值,如果format为最后的数据格式值,那么将返回0。由此不难写出处理剪贴板中所有格式数据项的程序段代码:

UINT format = 0; // 从第一种格式值开始枚举

OpenClipboard(hWnd);

while(format = EnumClipboardFormats(format))

{

…… // 对相关格式数据的处理

}

CloseClipboard();

在数据提供进程创建了剪贴板数据后,一直到有其他进程获取剪贴板数据前,这些数据都要占据内存空间。如在剪贴板放置的数据量过大,就会浪费内存空间,降低对资源的利用率。为避免这种浪费,可以采取延迟提交(Delayed rendering)技术,即由数据提供进程先创建一个指定数据格式的空(NULL)剪贴板数据块,直到有其他进程需要数据或自身进程要终止运行时才真正提交数据。

延迟提交的实现并不复杂,只需剪贴板拥有者进程在调用SetClipboardData()将数据句柄参数设置为NULL即可。延迟提交的拥有者进程需要做的主要工作是对WM_RENDERFORMAT、WM_DESTORYCLIPBOARD和WM_RENDERALLFORMATS等剪贴板延迟提交消息的处理。

当另一个进程调用GetClipboardData()函数时,系统将会向延迟提交数据的剪贴板拥有者进程发送WM_RENDERFORMAT消息。剪贴板拥有者进程在此消息的响应函数中应使用相应的格式和实际的数据句柄来调用SetClipboardData()函数,但不必再调用OpenClipboard()和EmptyClipboard()去打开和清空剪贴板了。在设置完数据有也无须调用CloseClipboard()关闭剪贴板。如果其他进程打开了剪贴板并且调用EmptyClipboard()函数去清空剪贴板的内容,接管剪贴板的拥有权时,系统将向延迟提交的剪贴板拥有者进程发送WM_DESTROYCLIPBOARD消息,以通知该进程对剪贴板拥有权的丧失。而失去剪贴板拥有权的进程在收到该消息后则不会再向剪贴板提交数据。另外,在延迟提交进程在提交完所有要提交的数据后也会收到此消息。如果延迟提交剪贴板拥有者进程将要终止,系统将会为其发送一条WM_RENDERALLFORMATS消息,通知其打开并清除剪贴板内容。在调用SetClipboardData()设置各数据句柄后关闭剪贴板。

下面这段代码将完成对数据的延迟提交,WM_RENDERFORMAT消息响应函数OnRenderFormat()并不会立即执行,当有进程调用GetClipboardData()函数从剪贴板读取数据时才会发出该消息。在消息处理函数中完成对数据的提交:

进行延迟提交:

HWND hWnd = GetSafeHwnd(); // 获取安全窗口句柄

::OpenClipboard(hWnd); // 打开剪贴板

::EmptyClipboard(); // 清空剪贴板

::SetClipboardData(CF_TEXT, NULL); // 进行剪贴板数据的延迟提交

::CloseClipboard(); // 关闭剪贴板

在WM_RENDERFORMAT消息的响应函数中:

DWORD dwLength = 100; // 要复制的字串长度

HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 分配内存块

LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 锁定内存块

for (int i = 0; i < dwLength; i++) // 将"*"复制到全局内存块

*lpGlobalMemory++ = '*';

GlobalUnlock(hGlobalMemory); // 锁定内存块解锁

::SetClipboardData(CF_TEXT, hGlobalMemory); // 将内存中的数据放置到剪贴板

DSP和自定义数据格式的使用

Windows系统预定义了三个带“DSP”前缀的数据格式:CF_DSPTEXT、CF_DSPBITMAP和CF_DSPMETAFILEPICT。这是一些伪标准格式,用于表示在程序中定义的私有剪贴板数据格式。对于不同的程序,这些格式的规定是不同的,因此这些格式只针对某一具体程序的不同实例才有意义。

为使用DSP数据格式,必须确保进程本身与剪贴板拥有者进程同属一个程序。可以调用GetClipboardOwner()函数来获取剪贴板拥有者窗口句柄,并调用GetClassName()来获取窗口类名:

HWND hClipOwner = GetClipboardOwner();

GetClassName(hClipOwner, &ClassName, 255);

如果剪贴板拥有者窗口类名同本进程的窗口类名一致,就可以使用带有DSP前缀的剪贴板数据格式了。

除了使用Windows预定义的剪贴板数据格式外,也可以在程序中使用自定义的数据格式。对于自定义的数据格式lpszFormat,可以调用RegisterClipboardFormat()函数来登记,并获取其返回的格式标识值:

UINT format = RegisterClipboardFormat(lpszFormat);

对此返回的格式标识值的使用与系统预定义的格式标识是一样的。可以通过GetClipboardFormatName()函数来获取自定义格式的ASCII名。

转载于:https://www.cnblogs.com/pavkoo/p/3324153.html

【笨嘴拙舌WINDOWS】剪切板相关推荐

  1. Windows 剪切板的应用——复制浏览器or本地目录图片

    一.简述 最近看了windows的剪切板相关的应用代码,于是乎,便想将浏览器中复制的图片保存到本地,经过尝试,通过以下代码完成在浏览器中右键选择复制图片,然后监控键盘事件,当按下Ctrl + v 时保 ...

  2. windows剪切板的历史记录

    windows剪切板的历史记录 最近遇到一件比较坑的事情.当然可能也是我本人粗心大意了吧.但是这种事情难免要发生.比如说你要移动一个比较重要的东西,然后按了ctrl+x,但是之间因为别的事情耽搁了一下 ...

  3. windows剪切板api

    Windows 剪切板API详解 (一) ChangeClipboardChain  将剪贴的连接从一个句柄转到下一个句柄.  BOOL ChangeClipboardChain(  HWND hWn ...

  4. windows剪切板文本和文件的获取设置

    介绍 windows剪切板的内容包含很多不同的格式,例如:CF_TEXT.CF_BITMAP.CF_METAFILEPICT.CF_SYLK.CF_DIF.CF_TIFF.CF_OEMTEXT.CF_ ...

  5. 如何获取windows剪切板中内容

    一个简单的获取windows剪切板中文本内容的代码 // 获取剪切板中的内容public static String getClipboardText() {Clipboard clip = Tool ...

  6. 如何对Windows剪切板里的内容进行取证分析 Windows剪切板取证

    前言 无论是在现实中对设备进行取证分析,还是在ctf中做取证类的题目,剪切板里的内容都需要去查看,以免遗漏什么重要信息 剪切板位置 剪切板是计算机操作系统提供的一个临时存储区域,用于在不同应用程序之间 ...

  7. 写入windows剪切板,粘贴出来乱码

    操作windows剪切板,写入正常,粘贴出来时候乱码了. 之前的代码: BOOL CopyStringToClipBoard( HWND hOwner, CString strSource ) {if ...

  8. matlab复制矢量图形,Matlab4.2b提供了将wmf格式矢量图复制到Windows剪切板.doc

    Matlab4.2b提供了将wmf格式矢量图复制到Windows剪切板.doc Matlab 4.2b提供了将wmf格式矢量图复制到Windows剪切板的功能,可以将它直接粘贴到Word文档中而不发生 ...

  9. windows剪切板暂存

    其实最初是因为在项目中使用了html网页编辑器,通过ie的com组件和javascript通讯完成一些事情,其中有一个功能是插入表格,我们原本使用的range.pasteHTML(HTMLstr);根 ...

  10. 在Git for windows(MSYS2)中实现tmux和windows剪切板的共享功能

    一直以来个人开发环境中linux下tmux和系统剪切板的共享功能都是通过xclip机制实现的,废话不多说直接贴在用的tmux配置文件吧: # 显示vi-cope模式下的映射 tmux list-key ...

最新文章

  1. Centos配置终端的快捷键
  2. JS对象和JSON字符串相互转化总结
  3. C语言如何使用函数交换两个变量的值
  4. 关于SVN Server自助修改密码详细教程
  5. php 获得当月时间戳,php获取当前月与上个月月初及月末时间戳的方法
  6. mysql002创建表
  7. vue登录如何存储cookie_vue保持用户登录状态(各种token存储方式)
  8. 6、raid、lvm、while、until 学习笔记
  9. 百度C2C对决淘宝的两把利器
  10. CSDN自定义模块简单设置之——添加图片、文字、链接等
  11. GIS技巧100例25-ArcGIS之shp文件修复
  12. 温暖(warmth)
  13. php程序如何删除文件夹和文件
  14. 二次开发crmeb增加实名认证 20220331
  15. 基于XGBoost 的机器学习可解释性
  16. springboot毕设项目流动人口信息管理系统9i8kh(java+VUE+Mybatis+Maven+Mysql)
  17. postgresql 数据库 报错 FATAL: the database system is shutting down 解决方法
  18. 李宇春男朋友被曝光,藏得真好,这都快结婚了才透露!
  19. 彻底卸载node.js
  20. 联通定制机酷派7728root方法(含工具)其它版本通用

热门文章

  1. 10x程序员工作法 学习笔记
  2. 360天擎进入控制中心报错CDbConnection failed to open the DB connection.
  3. 超简单、超容易理解的随机纯数字生成器,传入要生成的随机数的位数即可(10位以下)
  4. 我在 ICU 病房的惊魂一夜
  5. 7-38 支票面额 (15 分) 一个采购员去银行兑换一张y元f分的支票,结果出纳员错给f元y分。采购员用去n分后发觉有错,余额有2y元2f分,问支票面额?PTA:中M2021春C、Java入门练习I
  6. vue项目中已拦截跨源请求:同源策略禁止读取位于....(原因:CORS 请求未能成功)解决方案
  7. 华为交换机测试吞吐量软件,【精选】ixchariot网络吞吐量测试软件.pdf
  8. Oracle数据回滚
  9. 处理 尚志 不足问题
  10. 运算符和表达式之逻辑和赋值