@
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