@


Windows剪贴板复制消息和内容截取

解决了什么问题

在Windows应用开发的过程中,根据需求有时候会需要监控一下剪贴板,看看剪贴板的内容是否是我们应用程序感兴趣的内容。
比如下载器,往往会监控剪贴板内是否有下载链接,然后弹出下载页面
本文将一探究竟,自己实现一个当剪贴板内容有更新时立即通知到我们程序


实现思路

当剪贴板有新的内容产生时,Windows会给关注的窗口推送剪贴板内容更新消息,我们需要在我们窗口中先注册监听剪贴板相关消息,我们需要关注的是剪贴板有新的内容进入的时候怎么做的问题,所以本文主要关注 WM_DRAWCLIPBOARD 消息。
关于windows剪贴板每个消息的含义,可以查看微软的文档 WM_DRAWCLIPBOARD 消息说明和接收该消息需要的操作.

实现过程

1. 监听剪贴板内容更新消息

SetClipboardViewer(); // 在本程序中打开的窗口中监听剪贴板消息

2. 捕获剪贴板消息

代码部分示例:

	BEGIN_MSG_MAP(Window)
		MESSAGE_HANDLER(WM_DRAWCLIPBOARD, OnClipboardMessage)
	END_MSG_MAP()

WM_DRAWCLIPBOARD 是剪贴板内容发生变动时的窗口消息,在本窗体中监听该消息,当触发这个消息时,可以获取一下剪贴板的内容。本实例中使用这个回调函数 OnClipboardMessage 来处理。

3. 获取剪贴板内容

获取剪贴板文本信息

CString result; //存放剪贴板文本信息
if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard())
{
	HANDLE hClipboard = GetClipboardData(CF_UNICODETEXT);
	if (hClipboard)
	{
		wchar_t* ptr = reinterpret_cast<wchar_t*>(GlobalLock(hClipboard)); 
		if (ptr != NULL) 
		{ 
			result = ptr;
			GlobalUnlock(hClipboard); 
		} 
	}		
	CloseClipboard();
}

获取剪贴板文件复制消息

if (IsClipboardFormatAvailable(CF_HDROP) && OpenClipboard())
{
	HDROP hDrop = HDROP(::GetClipboardData(CF_HDROP)); //获取剪切板中复制的文件列表相关句柄
	if (hDrop)
	{
		WCHAR szFilePathName[MAX_PATH + 1] = { 0 };
		UINT nNumOfFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); //得到文件个数
		// 考虑到用户可能同时选中了多个对象(可能既包含文件也包含文件夹),以要循环处理
		for (UINT i = 0; i < nNumOfFiles; ++i)
		{
			memset(szFilePathName, 0, MAX_PATH + 1);
			DragQueryFile(hDrop, i, szFilePathName, MAX_PATH);  //得到文件名
			std::wstring ss = szFilePathName;
			std::wcout << TEXT("文件:") << ss << std::endl; // 打印复制的文件名
		}
	}
	CloseClipboard();
}
剪贴板粘贴消息的一些思考

由于没有剪贴板粘贴时触发的相应消息,在粘贴时,是由应用进程来读取剪贴板内容来完成的粘贴操作,所以无法像捕获消息一样通过监听消息来直接捕获到粘贴操作。

根据日常的粘贴操作情况,需要捕获键盘 CTRL+V 消息,或者hook粘贴板读取函数
键盘钩子通过SetWindowsHookEx()或RegisterRawInputDevices()可以监视Ctrl+V击键。经由消息挂钩SetWindowsHookEx()可以监视与粘贴的窗口消息,如WM_PASTE,EM_PASTESPECIAL,WM_COMMAND等,但即使是这样,也没有检测每一个可能的粘贴操作的保证。

可能不得不求助于将代码注入目标进程,以直接挂钩查询剪贴板数据的各种 Win32 API 函数。然后,当目标进程尝试在任何类型的粘贴操作期间检索数据时,可以修改这个过程(例如让应用程序认为没有可粘贴的数据):

CountClipboardFormats()
EnumClipboardFormats()
IsClipboardFormatAvailable()
GetPriorityClipboardFormat()
GetUpdatedClipboardFormats()
GetClipboardData() 和 OleGetClipboard()

参考:https://stackoverflow.com/questions/46699066/clipboard-viewer-doesnt-get-paste-notification