在應(yīng)用程序中監(jiān)視剪貼板的變化與內(nèi)容
發(fā)表時間:2023-08-19 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]剪貼板是Windows中系統(tǒng)級的堆空間,系統(tǒng)中任何一個應(yīng)用程序?qū)糍N板都有訪問權(quán),可以通過剪貼板消息和使用剪貼板API來讀寫剪貼板內(nèi)容。因此使用剪貼板不僅可以在同一個應(yīng)用程序內(nèi)交互數(shù)據(jù),也可以在不通...
剪貼板是Windows中系統(tǒng)級的堆空間,系統(tǒng)中任何一個應(yīng)用程序?qū)糍N板都有訪問權(quán),可以通過剪貼板消息和使用剪貼板API來讀寫剪貼板內(nèi)容。因此使用剪貼板不僅可以在同一個應(yīng)用程序內(nèi)交互數(shù)據(jù),也可以在不通應(yīng)用程序之間交互數(shù)據(jù)。尤其是在不通應(yīng)用程序之間交互時,應(yīng)用程序往往需要對剪貼板內(nèi)容的變化做到實時感知,即應(yīng)用程序要能監(jiān)視剪貼板內(nèi)容的變化。
Windows應(yīng)用是消息驅(qū)動的,同理當(dāng)剪貼板內(nèi)容發(fā)生變化時,Windows提供了剪貼板變化消息,因此要實時感知剪貼板內(nèi)容的變化,關(guān)鍵是應(yīng)用程序要能響應(yīng)和處理Windows觸發(fā)的剪貼板變化消息。
第一步,要將窗口注冊為Clipboard Viewer
需要首先解釋兩個概念:Clipboard Viewer和Clipboard Viewer Chain。
Clipboard Viewer是一個需要取得并顯示剪貼板內(nèi)容的窗口,通過Clipboard Viewer這個機(jī)制,應(yīng)用程序可以在不影響剪貼板內(nèi)容的情況下獲取剪貼板的變化消息。Clipboard Viewer可以顯示系統(tǒng)定義的標(biāo)準(zhǔn)格式的剪貼板內(nèi)容,也可以顯示應(yīng)用自定義的私有數(shù)據(jù)格式的內(nèi)容。通過調(diào)用函數(shù)SetClipboardViewer將窗口注冊為Clipboard Viewer。
Clipboard Viewer Chain是保存Clipboard Viewer窗口以及他們之間的前后向關(guān)系的一個Windows系統(tǒng)鏈表,當(dāng)一個窗口注冊為Clipboard Viewer后,他會被加入Clipboard Viewer Chain,并得到鏈表中下一個Viewer窗口的句柄,該句柄必須保存以在響應(yīng)消息時使用,該句柄的作用在下文說明。Windows正是通過Clipboard Viewer Chain保證了所有Clipboard Viewer能接收和響應(yīng)剪貼板變化消息。
第二步,響應(yīng)剪貼板變化消息,判斷和取出剪貼板內(nèi)容
在消息響應(yīng)里必須正確處理兩個消息:WM_DRAWCLIPBOARD和WM_CHANGECBCHAIN。
當(dāng)剪貼板內(nèi)容發(fā)生變化時,Windows將觸發(fā)WM_DRAWCLIPBOARD消息,并將該消息送給Clipboard Viewer Chain的第一個窗口。每一個Clipboard Viewer窗口,包括第一個窗口在響應(yīng)和處理該消息后,必須根據(jù)其保存的鏈表中的下一個窗口的句柄將該消息發(fā)送給下一個Clipboard Viewer窗口。窗口可以在該消息中取出剪貼板內(nèi)容,并判斷是否是該窗口增在監(jiān)視的內(nèi)容,如果是就進(jìn)行相應(yīng)的處理。
當(dāng)某個Clipboard Viewer窗口注銷時,系統(tǒng)將觸發(fā)WM_CHANGECBCHAIN,并將該消息送給Clipboard Viewer Chain的第一個窗口。每一個窗口必須處理該消息。
第三步,將窗口從Clipboard Viewer Chain中注銷
當(dāng)窗口不再需要監(jiān)視剪貼板變化消息,或窗口要關(guān)閉時,必須調(diào)用ChangeClipboardChain函數(shù)將窗口從Clipboard Viewer Chain中注銷。注銷后系統(tǒng)會觸發(fā)WM_CHANGECBCHAIN消息,同WM_DRAWCLIPBOARD消息一樣,該消息會給發(fā)送給Clipboard Viewer Chain的第一個窗口處理。下面代碼示例當(dāng)窗口被關(guān)閉時進(jìn)行注銷。
下面的代碼片斷給出了監(jiān)視剪貼板中是否拷貝了URL地址的例子,如果剪貼板中的內(nèi)容是URL地址,則將其顯示在窗口界面上。為使示例代碼具有一般性,下面給出了一般Windows程序代碼和基于MFC的代代碼。其他語言要實現(xiàn)該功能可以參考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: //剪貼板內(nèi)容變化
//觸發(fā)ON_PAINT顯示URL內(nèi)容
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);
//判斷剪貼板中的內(nèi)容是否為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程序示例代碼
首先要映射以下消息和繼承以下函數(shù)
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;
}
//剪貼板內(nèi)容變化,判斷剪貼板中的內(nèi)容是否為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();
}
}