明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

在應(yīng)用程序中監(jiān)視剪貼板的變化與內(nèi)容

[摘要]剪貼板是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();

    }   

}