
论逼呼的精准推送……
这两天刚在 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
在其中存储其他每一项的额外数据。
<
div>
// 上面提到的相关结构 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;
Leave a Reply