当我们在电脑按下 ctrl+c,剪切板储存了哪些信息?


论逼呼的精准推送……

这两天刚在 steam 上架了一个相关的工具以填补 steam 上没有此类工具的空白,就刷到这么个问题。

Save 13% on Cliper: A clipboard enhancement tool on Steam


回到正题。首先,剪贴板本质上是一片共享内存,只不过跟普通的共享内存不太一样的是,其由操作系统管理各个程序的访问权限,并提供额外的高级功能。

简单解答问题正文中的提问,需要一些比喻,比喻不可避免的会损失一些细节信息,如果对程序实现有兴趣的话,文章最后我会写一些。

以问题中第二问举例剪贴板的交互

再或者,当我在浏览器中复制一段内容,粘贴到 word 中,word 甚至能保存文字图片原来在网页中的 html 样式。如果我粘贴到 onenote 中,还会显示原网页地址。

系统提供的剪贴板,类似于一个有很多格子的空储物柜,和一些标签。你在浏览器的网页中按下复制时,浏览器通常会:

  • 打开贴着标签"文本"的柜格,把选中的内容中的文本扔进去。[1]
  • 再找一个空柜格,把选中内容所关联的 html 代码扔进去,找个空标签,写上"html 代码",贴在柜子上。
  • 再找一个空柜格,把选中部分的详细信息,如起始位置、结束位置、来源于哪个网站,这些信息扔进去,找个空标签写上"html 详细信息",贴在柜子上。

然后,你在记事本里进行了粘贴,发生了这些事:

  • 记事本扫了一眼整个柜子,发现其中三个有标签有内容,其中包含写着"文本"的。
  • 记事本不管其他格式,径直打开文本的柜格取出内容并把它放到了编辑框里。

或者,你在 word 里按下了粘贴,发生了这些事:

  • word 扫了一眼整个柜子,发现其中三个有标签有内容。
  • 发现了有写着"html 代码"的标签,根据预设的规则,优先将其取出。
  • 将取出的内容按网页解析,分析出了布局,在自己的编辑框里布置好了。

而到了 onenote,其实跟上面差不多,只是他发现有"html 代码"的情况下,还会去看看有没有"html 详细信息",如果有,从里面翻出网址并显示

总结来说,就是剪贴板里可以放多种数据,复制时,"来源程序"可以随自己喜好,放多种格式在里面,粘贴时,"目标程序"可以根据自己需求,提取其中一种或数种并处理,然后展示给用户。

另外,这个放柜子的屋子是有反锁的,通常来说屋子里只能有一个人存放或者取出东西,以防混乱。系统剪贴板基本上就是这样一个东西。

再说题目中的第一问

如题,比如我使用远程桌面,在远程主机复制一个文件,然后粘贴到我的电脑,这个过程中,我按下复制的时候,剪切板保存的是什么信息?

绝大部分情况下,都是远程软件的远程端,读取了远程电脑的剪贴板,并且以数据形式发给了本地端,本地端再把数据还原回来,存入剪贴板。也就是说这个过程其实跟系统剪贴板没关系,相当于你复制了东西,粘贴到了 qq 里发送,另一个人再从 qq 里复制出来,此时你的电脑和他的电脑剪贴板里的东西就是相同的了,但是实际上剪贴板没做什么特殊的。(当然 qq 只能发文本,而远程软件把所有类型都处理了)


相关技术细节

windows 剪贴板的 API 流程很长,并且最终的部分是在内核中处理的,以读取剪贴板图片为例的主要流程。

OleGetClipboard (IDataObject::GetData)

->

GetClipboardData

->

用户层 NtUserGetClipboardData

->

Wow64 层

->

Shadow SSDT

->

内核层 NtUserGetClipboardData

->

xxxGetClipboardData

->

xxxGetDummyBitmap

->

xxxDIBtoBMP

,然后拿着一个句柄一路回来。

其中用户层前两层都是公开了接口的,即 COM(OLE)层的

OleGetClipboard

系列函数[2][3],和更底层来自 User32 的

GetClipboardData

系列函数[4]

基于众所周知令人头大的 COM 层的前者,提供了比后者更多的功能,比如说给每项剪贴板子类型一些额外的标注,图片缩略图等[5],但也继承了 COM 层的繁杂。起初我不知道这些额外的信息是存在哪的,疑惑了很久,后来做了逆向发现它只是自己做了个子格式

Ole Private Data

在其中存储其他每一项的额外数据。

// 上面提到的相关结构
typedef struct tagFORMATETC {
  CLIPFORMAT     cfFormat;
  DVTARGETDEVICE *ptd;
  DWORD          dwAspect;
  LONG           lindex;
  DWORD          tymed;
} FORMATETC, *LPFORMATETC;

typedef enum tagDVASPECT {
  DVASPECT_CONTENT,
  DVASPECT_THUMBNAIL,
  DVASPECT_ICON,
  DVASPECT_DOCPRINT
} DVASPECT;

typedef enum tagTYMED {
  TYMED_HGLOBAL,
  TYMED_FILE,
  TYMED_ISTREAM,
  TYMED_ISTORAGE,
  TYMED_GDI,
  TYMED_MFPICT,
  TYMED_ENHMF,
  TYMED_NULL
} TYMED;

使用起来,读写大致就是构造一个

IDataObject

,在里面存储数据(期间要构造成吨的类似上面的烦人结构),然后调用

OleSetClipboard

写入剪贴板,或者调用

OleGetClipboard

从剪贴板取回一个此格式并从里面读数据。

--

而后者,来自

User32

的 api 系列,就是很普通的 windows api 的样子了,有点繁杂,但不像前者那样恶心,不过用起来也不是很好受,你甚至不知道 GetData 回来的到底是个什么东西,是个句柄?是个指针?指向什么的指针?只能自己预设一个表,根据不同的类型自己解析,并且还要调用不同的释放资源 api。用法有其他答主列出了我就不多说了。

--

不得不吐槽,不知是是历史兼容问题还是设计眼界问题,剪贴板这套系统非常的不优雅,甚至可以说稀烂。比如说跟窗口强关联(是的,你的程序如果没有窗口,很多剪贴板相关的 api 都不能用)、使用着 GlobalAlloc 系列古老的函数、来源和目标程序各自使用上面两者的兼容等问题。

也没有一个封装好的库能完美的解决这些,比如说 c#自带的库,使用了 COM 层的接口,但是暴露的数据却跟 User32 的接口差不多,看不到那些额外数据。而且稳定性很差,随随便便都会冒出一大堆晦涩难懂的 COM 层异常,比如说随便去 Excel 里框几格表并复制,然后执行下面的代码,就是读出剪贴板并写回(几次),然后再贴到一个记事本里,多半粘贴出来的内容不对,会抛出异常,甚至你的 c#程序还会崩掉,try 不到的那种。

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 3; i++)
            {
                var foo = Clipboard.GetDataObject();
                Clipboard.SetDataObject(foo, true);
            }
        }
        
        // 补充一个更容易出问题的,不直接写回去,而是把剪贴板里所有类型读出
        // 放到一个新的容器里,再写回去
        private void button2_Click(object sender, EventArgs e)
        {
            var oldDataObject = Clipboard.GetDataObject();
            var newDataObject = new DataObject();

            foreach (var formatName in oldDataObject.GetFormats())
            {
                var data = oldDataObject.GetData(formatName);
                newDataObject.SetData(formatName, data);
            }

            Clipboard.SetDataObject(newDataObject, true);
        }
刻在人类基因中里的禁令有哪些?(人类刻在基因中的禁忌是真的吗)
上一篇
为什么行星有体积极限?(行星为什么都是球体)
下一篇
本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

相关推荐

  • 苹果手机各个功能介绍,iphone必须关闭的十个功能

    1、关闭蓝牙。现在已经很少有人用蓝牙传输文件了,而且iPhone与安卓的蓝牙并不兼容,所以,可以在设置中,关闭蓝牙功能。2、关闭通知功能。关于APP推送,无非也就是一些更新提醒,关了也不会有什么影响,还能多省点电。3、关闭自动调节亮度功能。一般来说,可以将屏幕亮度在15%-30%之间,在强光环境中,在进行手动调整就可以了。4、禁止后台刷新。在设置—通用中,关闭后台自动刷新功能,也可以对省电起到一点...

  • 高德打车怎么设置途经地,高德如何添加途经路线

    1、点击高德地图APP界面底部的“导航”按钮,进入导航模式。2、点击右下角的“路线”,进入路线设定页面,根据要求输入起点、终点进行路线规划。3、点击“添加途经点”,弹出添加途经点页面,点击右上角,可以添加或者删除途经点,乘客可以手动输入要添加的途经点。4、当添加完途经点时,点击“确定”按钮,即可添加途经路线。此时地图会显示出这条路线上所有的途经点,以及当前途经点的地点信息。怎么设计高德地图设置要经...

  • 高中必修二物理知识点总结,高一物理必修2重点知识点归纳

    您好,1.运动学-位移、速度、加速度的概念及计算方法-相关运动的分析方法,如相对运动和抛体运动-牛顿运动定律及其应用2.力学-力的概念及种类,如重力、弹力、摩擦力等-牛顿第一、二、三定律及其应用-力的合成与分解-能量、功、动能定理、功率的概念及计算方法-动量、冲量定理及其应用3.热学-温度、热量、热能的概念及计量单位-热传递的方式及其特点,如传导、对流、辐射-热力学第一、二定律及其应用,如热机效率...

  • 刻在人类基因中里的禁令有哪些?(刻在基因上的禁令)

    很好的问题,但是这个问题下的回答都答得太感性了。近现代的心理学研究是进入了人类大脑领域的,如果你认为那些基因阻止人类有意或无意的行为就算是禁令的话,那与脑部相关的基因每一条都写满了“禁止”两个字。第一...

  • (1/1)^2+(1/2)^2+(1/3)^2+……=(π^2)/6,为什么结果与 π 有关?

    3Blue1Brown 给了一个非常漂亮的几何解释:大概思路是用反比平方定律把问题物理化。假设你在一个周长为 2 的圆形湖上,其直径为假设你在这个环形湖对面有个点光源,那么根据反比平方定律,你感知的光...