監(jiān)視剪貼板內(nèi)容變化的程序叫“剪貼板查看器”。在Windows中已經(jīng)擁有了剪貼板查看器,當(dāng)然也可以用API函數(shù)編寫自己的剪貼板查看器程序。 8.3.1 剪貼板查看器鏈接列表 在Windows下可以同時(shí)運(yùn)行很多剪貼板查看器,用于時(shí)刻監(jiān)視剪貼板內(nèi)容的變化情況。但Windows在剪貼板內(nèi)容發(fā)生變化時(shí),只對(duì)一個(gè)剪貼板查看器窗口發(fā)送消息,這就是所謂的“當(dāng)前剪貼板查看器”。 應(yīng)用程序想要得到Windows發(fā)送給當(dāng)前剪貼板查看器的消息,就必須加入“剪貼板查看器鏈接列表”。當(dāng)一個(gè)程序?qū)⒆约鹤?cè)為一個(gè)剪貼板查看器時(shí),其窗口句柄被Windows留作當(dāng)前剪貼板查看器窗口句柄,它也就被Windows設(shè)置為當(dāng)前剪貼板查看器。當(dāng)此程序收到一個(gè)剪貼板查看器消息時(shí),就把這個(gè)消息發(fā)送給剪貼板鏈表中下一個(gè)程序的窗口過程。 8.3.2 有關(guān)剪貼板查看器的函數(shù)和消息1. 剪貼板查看器函數(shù)消息介紹 程序通過調(diào)用函數(shù)SetClipboardViewer成為剪貼板查看器鏈表中的一員。如果程序是作為剪貼板查看器使用的,那么在主窗口創(chuàng)建之初就應(yīng)該調(diào)用此函數(shù)。即在處理消息WM_CREATE期間,函數(shù)返回前一個(gè)當(dāng)前剪貼板查看器的窗口句柄,將其放在靜態(tài)變量中,如下所示: static HWND hWndNextViewer; case WM_CREATE : hWndNextViewer = SetClipboardViewer(hWnd); 在以后介紹的實(shí)例中,大家將看到具體的應(yīng)用。 一旦成為當(dāng)前剪貼板查看器,只要剪貼板有任何變化,Windows都會(huì)把WM_DRAWCLIPBOARD消息發(fā)送給其窗口過程。剪貼板查看器鏈表中的每一個(gè)程序,都應(yīng)該用SendMessage把這個(gè)消息發(fā)送給下一個(gè)剪貼板查看器。鏈表中的最后一個(gè)程序,就是當(dāng)前剪貼板查看器的窗口所保存的hWndNextViewer是NULL。當(dāng)消息傳給它后,程序只是簡單地返回,消息不再從這里發(fā)出。具體的處理方法是除了當(dāng)前剪貼板查看器,一般都是簡單地將消息發(fā)送給下一個(gè)剪貼板查看器的窗口過程,并使本窗口的客戶區(qū)無效,如下所示: case WM_DRAWCLIPBOARD : if( hWndNextViewer ) SendMessage ( hWndNextViewer, message, wParam, lParam); InvalidateRect( hWnd, NULL, TRUE); Break; 在處理WM_PAINT期間,可以調(diào)用OpenClipboard,GetClipboardData和CloseClipboard讀取剪貼板的內(nèi)容。當(dāng)一個(gè)程序要從剪貼板查看器鏈表中刪除自己時(shí),應(yīng)當(dāng)調(diào)用ChangeClipboardChain函數(shù)。函數(shù)原型如下: BOOL ChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext ); 其中,第一個(gè)參數(shù)hWndRemove是從剪貼板查看器鏈中刪除的窗口句柄;第二個(gè)參數(shù)hWndNewNext是剪貼板查看器鏈中hWndRemove后面緊跟的窗口句柄。當(dāng)程序調(diào)用ChangeClipboardChain時(shí),Windows將消息 WM_CHANGECBCHAIN發(fā)送到當(dāng)前剪貼板查看器。在此消息中,參數(shù)wParam是函數(shù)ChangeClipboardChain的第一個(gè)參數(shù),參數(shù)lParam是函數(shù)ChangeClipboardChain的第二個(gè)參數(shù)。 當(dāng)程序收到消息WM_CHANGECBCHAIN,必須檢查wParam與保留的hWndNextViewer靜態(tài)變量是否相同。如果相同,則將hWndNextViewer設(shè)置為lParam。此項(xiàng)工作保證了將來WM_DRAWCLIPBOARD消息不會(huì)被發(fā)送到一個(gè)已經(jīng)不存在的窗口。如果不同,并且hWndNextViewer不為NULL,表明非當(dāng)前剪貼板查看器,則將消息發(fā)送給下一個(gè)剪貼板查看器,如下所示: case WM_CHANGECBCHAIN : if( (HWND) wParam ==hWndClipboardViewer ) hWndClipboardViewer = (HWND) lParam; else if (hWndClipboardViewer) SendMessage ( hWndClipboardViewer, message, wParam, lParam); return 0; 當(dāng)程序要結(jié)束時(shí),如果它仍然在剪貼板查看器鏈中,則必須將其刪除。為了保證這一點(diǎn),可以在處理消息WM_DESTORY時(shí)通過調(diào)用函數(shù)ChangeClipboardChain來實(shí)現(xiàn),如下所示: case WM_DESTORY : ChangeClipboardChain( hWnd, hWndNextViewer ); PostQuitMessage( 0); break; Windows還提供獲得第一個(gè)剪貼板查看器窗口句柄的函數(shù): HWND GetClipboardViewer(VOID); 一般來說不會(huì)用到這個(gè)函數(shù)。如果沒有當(dāng)前剪貼板查看器,那么此函數(shù)的返回值為NULL。 2. 剪貼板查看器的實(shí)例分析 下面來看一個(gè)剪貼板查看器的具體實(shí)例,這個(gè)實(shí)例提供了顯示CF_BITMAP和CF_TEXT的剪貼板查看器。雖然它只提供了Windows剪貼板查看器的部分功能,但從中可以了解剪貼板查看器是如何工作的。 首先,在創(chuàng)建窗口時(shí)就設(shè)置本窗口為剪貼板查看器鏈表中的一員,其中函數(shù)SetOwner是用在窗口標(biāo)題表明客戶區(qū)的內(nèi)容來自何處。其中,用到了函數(shù)GetClipboardOwner,如下所示: case WM_CREATE: hNextViewer = SetClipboardViewer( hWnd ); SetOwner( hWnd ); break; 其次,看消息是如何在剪貼板查看器鏈表中傳遞的,如下所示: case WM_DRAWCLIPBOARD: if (hNextViewer) SendMessage( hNextViewer, WM_DRAWCLIPBOARD, wParam, lParam ); InvalidateRect( hWnd, NULL, TRUE ); SetOwner( hWnd ); break; 再看應(yīng)用程序是如何將自己從剪貼板查看器鏈表中刪除的,如下所示: case WM_CHANGECBCHAIN: if ( (HWND)wParam == hNextViewer ) hNextViewer = (HWND)lParam; else if ( hNextViewer ) SendMessage( hNextViewer, WM_CHANGECBCHAIN, wParam, lParam ); break; 最后是介紹在消息WM_PAINT中實(shí)現(xiàn)了什么。真正的具體實(shí)現(xiàn)是在這里完成的,在前面已經(jīng)介紹了有關(guān)函數(shù)的具體操作,讀者看起來應(yīng)該輕松一些。 case WM_PAINT : { static HANDLE hMem; // 剪貼板文本的句柄 static HBITMAP hBitmap; // 剪貼板位圖的句柄 RECT rect; // 客戶區(qū) PAINTSTRUCT ps; // 繪圖的結(jié)構(gòu) BeginPaint( hWnd, &ps ); GetClientRect( hWnd, &rect ); OpenClipboard( hWnd ); if (hMem = GetClipboardData( CF_TEXT )) // 如果剪貼板中的內(nèi)容為文本格式,則執(zhí)行 { LPSTR lpMem = GlobalLock( hMem ); DrawText( ps.hdc, lpMem, -1, &rect, DT_LEFT ); GlobalUnlock( hMem ); } // 如果非文本格式,測(cè)試是否為位圖格式 else if (hBitmap = GetClipboardData( CF_BITMAP )) { BITMAP bm; HDC hMemDC = CreateCompatibleDC( ps.hdc ); SelectObject( hMemDC, hBitmap ); GetObject( hBitmap, sizeof (BITMAP), (LPSTR) &bm ); BitBlt( ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY ); DeleteDC( hMemDC ); } CloseClipboard( ); EndPaint( hWnd, &ps ); } break; 通過本章的學(xué)習(xí),了解了如何運(yùn)用API提供的函數(shù)來編寫剪貼板的程序。在實(shí)際的編程中會(huì)經(jīng)常用到剪貼板的內(nèi)容,希望通過本章的學(xué)習(xí),讀者在編寫程序時(shí)能如虎添翼。
|