在應用程序中監視剪貼板的變化與內容
發表時間:2023-08-19 來源:明輝站整理相關軟件相關文章人氣:
[摘要]剪貼板是Windows中系統級的堆空間,系統中任何一個應用程序對剪貼板都有訪問權,可以通過剪貼板消息和使用剪貼板API來讀寫剪貼板內容。因此使用剪貼板不僅可以在同一個應用程序內交互數據,也可以在不通...
剪貼板是Windows中系統級的堆空間,系統中任何一個應用程序對剪貼板都有訪問權,可以通過剪貼板消息和使用剪貼板API來讀寫剪貼板內容。因此使用剪貼板不僅可以在同一個應用程序內交互數據,也可以在不通應用程序之間交互數據。尤其是在不通應用程序之間交互時,應用程序往往需要對剪貼板內容的變化做到實時感知,即應用程序要能監視剪貼板內容的變化。
Windows應用是消息驅動的,同理當剪貼板內容發生變化時,Windows提供了剪貼板變化消息,因此要實時感知剪貼板內容的變化,關鍵是應用程序要能響應和處理Windows觸發的剪貼板變化消息。
第一步,要將窗口注冊為Clipboard Viewer
需要首先解釋兩個概念:Clipboard Viewer和Clipboard Viewer Chain。
Clipboard Viewer是一個需要取得并顯示剪貼板內容的窗口,通過Clipboard Viewer這個機制,應用程序可以在不影響剪貼板內容的情況下獲取剪貼板的變化消息。Clipboard Viewer可以顯示系統定義的標準格式的剪貼板內容,也可以顯示應用自定義的私有數據格式的內容。通過調用函數SetClipboardViewer將窗口注冊為Clipboard Viewer。
Clipboard Viewer Chain是保存Clipboard Viewer窗口以及他們之間的前后向關系的一個Windows系統鏈表,當一個窗口注冊為Clipboard Viewer后,他會被加入Clipboard Viewer Chain,并得到鏈表中下一個Viewer窗口的句柄,該句柄必須保存以在響應消息時使用,該句柄的作用在下文說明。Windows正是通過Clipboard Viewer Chain保證了所有Clipboard Viewer能接收和響應剪貼板變化消息。
第二步,響應剪貼板變化消息,判斷和取出剪貼板內容
在消息響應里必須正確處理兩個消息:WM_DRAWCLIPBOARD和WM_CHANGECBCHAIN。
當剪貼板內容發生變化時,Windows將觸發WM_DRAWCLIPBOARD消息,并將該消息送給Clipboard Viewer Chain的第一個窗口。每一個Clipboard Viewer窗口,包括第一個窗口在響應和處理該消息后,必須根據其保存的鏈表中的下一個窗口的句柄將該消息發送給下一個Clipboard Viewer窗口。窗口可以在該消息中取出剪貼板內容,并判斷是否是該窗口增在監視的內容,如果是就進行相應的處理。
當某個Clipboard Viewer窗口注銷時,系統將觸發WM_CHANGECBCHAIN,并將該消息送給Clipboard Viewer Chain的第一個窗口。每一個窗口必須處理該消息。
第三步,將窗口從Clipboard Viewer Chain中注銷
當窗口不再需要監視剪貼板變化消息,或窗口要關閉時,必須調用ChangeClipboardChain函數將窗口從Clipboard Viewer Chain中注銷。注銷后系統會觸發WM_CHANGECBCHAIN消息,同WM_DRAWCLIPBOARD消息一樣,該消息會給發送給Clipboard Viewer Chain的第一個窗口處理。下面代碼示例當窗口被關閉時進行注銷。
下面的代碼片斷給出了監視剪貼板中是否拷貝了URL地址的例子,如果剪貼板中的內容是URL地址,則將其顯示在窗口界面上。為使示例代碼具有一般性,下面給出了一般Windows程序代碼和基于MFC的代代碼。其他語言要實現該功能可以參考Windows程序代碼。兩個DEMO的完成代碼請見附件。
Windows程序示例代碼
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
unsigned int anFormats[] = {CF_TEXT};
unsigned int nFormat;
switch (message)
{
//----------------------------------------------------------------
case WM_CREATE:
//將本窗口注冊到Clipboard Viewer Chain,
//并保存Clipboard Viewer Chain中下一個窗口的句柄
hwndNextViewer = SetClipboardViewer(hWnd);
break;
case WM_CHANGECBCHAIN: //Clipboard Viewer注銷
//如果注銷的Clipboard Viewer窗口是本窗口的下一個窗口,
//則修改本窗口保存的下一個窗口句柄,
//否則將該消息傳遞到Clipboard Viewer Chain的下一個窗口
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, message, wParam, lParam);
break;
case WM_DRAWCLIPBOARD: //剪貼板內容變化
//觸發ON_PAINT顯示URL內容
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
//否則將該消息傳遞到Clipboard Viewer Chain的下一個窗口
SendMessage(hwndNextViewer, message, wParam, lParam);
break;
case WM_DESTROY:
//從Clipboard Viewer Chain注銷本窗口
ChangeClipboardChain(hWnd, hwndNextViewer);
PostQuitMessage(0);
break;
//----------------------------------------------------------------
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX,
hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
//判斷剪貼板中的內容是否為URL地址,如是則顯示
nFormat = GetPriorityClipboardFormat(anFormats,sizeof(anFormats));
if(nFormat == CF_TEXT)
{
OpenClipboard(hWnd);
HGLOBAL hMem = GetClipboardData(nFormat);
LPTSTR lpstr = (LPTSTR)GlobalLock(hMem);
if(strstr(lpstr,"http://") != NULL
strstr(lpstr,"ftp://") != NULL
strstr(lpstr,"file://") != NULL)
{
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, lpstr, -1, &rt, DT_LEFT);
}
GlobalUnlock(hMem);
CloseClipboard();
}
EndPaint(hWnd, &ps);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
MFC程序示例代碼
首先要映射以下消息和繼承以下函數
afx_msg void OnChangeCbChain(HWND hWndRemove, HWND hWndAfter);
afx_msg void OnDrawClipboard();
afx_msg void OnDestroy();
virtual void OnInitialUpdate();
void CMonitorUrlView::OnInitialUpdate()
{
CListView::OnInitialUpdate();
m_pListCtrl = &GetListCtrl();;
m_pListCtrl->SetExtendedStyle(LVS_EX_FULLROWSELECT
LVS_EX_GRIDLINES
LVS_EX_TRACKSELECT
LVS_EX_TWOCLICKACTIVATE
LVS_EX_UNDERLINECOLD);
m_pListCtrl->ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
m_pListCtrl->InsertColumn(0, "URL",LVCFMT_LEFT,600,1);
//將本窗口注冊到Clipboard Viewer Chain,
//并保存Clipboard Viewer Chain中下一個窗口的句柄
m_hwndNextViewer = SetClipboardViewer();
}
void CMonitorUrlView::OnDestroy()
{
CListView::OnDestroy();
//從Clipboard Viewer Chain注銷本窗口
ChangeClipboardChain(m_hwndNextViewer);
}
//Clipboard Viewer注銷
void CMonitorUrlView::OnChangeCbChain(HWND hWndRemove, HWND hWndAfter)
{
//如果注銷的Clipboard Viewer窗口是本窗口的下一個窗口,
//則修改本窗口保存的下一個窗口句柄,
CView::OnChangeCbChain(hWndRemove,hWndAfter);
if(hWndRemove == m_hwndNextViewer)
m_hwndNextViewer = hWndAfter;
}
//剪貼板內容變化,判斷剪貼板中的內容是否為URL地址,如是則顯示
void CMonitorUrlView::OnDrawClipboard()
{
CView::OnDrawClipboard();
unsigned int anFormats[] = {CF_TEXT};
unsigned int nFormat =
GetPriorityClipboardFormat(anFormats,sizeof(anFormats));
if(nFormat == CF_TEXT)
{
HGLOBAL hMem;
OpenClipboard();
if(hMem = ::GetClipboardData(CF_TEXT))
{
LPTSTR lpszText = (LPTSTR) GlobalLock(hMem);
CString strURL = lpszText;
strURL = strURL.SpanExcluding("\r\n");
if(strURL.Left(7).CompareNoCase("http://") == 0
strURL.Left(6).CompareNoCase("ftp://") == 0
strURL.Left(7).CompareNoCase("file://") == 0)
{
m_pListCtrl->InsertItem(0,lpszText);
}
GlobalUnlock(hMem);
}
CloseClipboard();
}
}