天天看點

VC++ MFC 使用剪切闆的方法

Windows剪貼闆是一種比較簡單同時也是開銷比較小的IPC(InterProcess Communication,程序間通訊)機制。Windows系統支援剪貼闆IPC的基本機制是由系統預留的一塊全局共享記憶體,用來暫存在各程序間進行交換的資料:提供資料的程序建立一個全局記憶體塊,并将要傳送的資料移到或複制到該記憶體塊;接受資料的程序(也可以是提供資料的程序本身)擷取此記憶體塊的句柄,并完成對該記憶體塊資料的讀取。

為使剪貼闆的這種IPC機制更加完善和便于使用,需要解決好如下三個問題:提供資料的程序在結束時 Windows系統将删除其建立的全局記憶體塊,而接受資料的程序則希望在其退出後剪貼闆中的資料仍然存在,可以繼續為其他程序所擷取;能友善地管理和傳送剪貼闆資料句柄;能友善設定和确定剪貼闆資料格式。為完善上述功能,Windows提供了存在于USER32.dll中的一組API函數、消息和預定義資料格式等,并通過對這些函數、消息的使用來管理在程序間進行的剪貼闆資料交換。

Windows系統為剪貼闆提供了一組API函數和多種消息,基本可以滿足程式設計的需要。而且Windows還為剪貼闆預定義了多種資料格式。通過這些預定義的格式,可以使接收方正确再現資料提供方放置于剪貼闆中的資料内容。

文本剪貼闆和位圖剪貼闆的使用

這兩種剪貼闆是比較常用的。其中,文本剪貼闆是包含具有格式CF_TEXT的字元串的剪貼闆,是最經常使用的剪貼闆之一。在文本剪貼闆中傳遞的資料是不帶任何格式資訊的ASCII字元。若要将文本傳送到剪貼闆,可以先配置設定一個可移動全局記憶體塊,然後将要複制的文本内容寫入到此記憶體區域。最後調用剪貼闆函數将資料放置到剪貼闆:

注意:

     下面代碼中:

     1、HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1);

配置設定記憶體長度必須是dwLength + 1,否則在調用SetClipboardData時會出錯;可以通過調用 int i2 = ::GlobalSize(hGlobalMemory );來檢視配置設定的長度。

     2、hGlobalMemory不能釋放,即不能調用::GlobalFree(hGlobalMemory),因為如果在一個程式中還要粘貼的話就必須不能釋放,否則在同一程式中粘貼時獲得的指針為NULL,但如果隻在别的程式中粘貼則可以釋放。

     以上兩條是在編制HsfBrowserCtl(HOOPS三維浏覽控件)時總結出來的。可參看原碼中的複制、粘貼部分。

DWORD dwLength = 100; // 要複制的字串長度

HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 配置設定記憶體

LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 鎖定記憶體

for (int i = 0; i 〈 dwLength; i++) // 将"*"複制到全局記憶體塊

*lpGlobalMemory++ = '*';

GlobalUnlock(hGlobalMemory); // 鎖定記憶體塊解鎖

HWND hWnd = GetSafeHwnd(); // 擷取安全視窗句柄

::OpenClipboard(hWnd); // 打開剪貼闆

::EmptyClipboard(); // 清空剪貼闆

::SetClipboardData(CF_TEXT, hGlobalMemory); // 将記憶體中的資料放置到剪貼闆

::CloseClipboard(); // 關閉剪貼闆

這裡以OpenClipboard()打開剪貼闆,并在調用了EmptyClipboard()後使hWnd指向的視窗成為剪貼闆的擁有者,一直持續到 CloseClipboard()函數的調用。在此期間,剪貼闆為擁有者所獨占,其他程序将無法對剪貼闆内容進行修改。

從剪貼闆擷取文本的過程與之類似,首先打開剪貼闆并擷取剪貼闆的資料句柄,如果資料存在就拷貝其資料到程式變量。由于GetClipboardData()擷取的資料句柄是屬于剪貼闆的,是以使用者程式必須在調用CloseClipboard()函數之前使用它:

HWND hWnd = GetSafeHwnd(); // 擷取安全視窗句柄

::OpenClipboard(hWnd); // 打開剪貼闆

HANDLE hClipMemory = ::GetClipboardData(CF_TEXT);// 擷取剪貼闆資料句柄

DWORD dwLength = GlobalSize(hClipMemory); // 傳回指定記憶體區域的目前大小

LPBYTE lpClipMemory = (LPBYTE)GlobalLock(hClipMemory); // 鎖定記憶體

m_sMessage = CString(lpClipMemory); // 儲存得到的文本資料

GlobalUnlock(hClipMemory); // 記憶體解鎖

::CloseClipboard(); // 關閉剪貼闆

大多數應用程式對圖形資料采取的是位圖的剪貼闆資料格式。位圖剪貼闆的使用與文本剪貼闆的使用是類似的,隻是資料格式要指明為CF_BITMAP,而且在使用SetClipboardData()或GetClipboardData()函數時交給剪貼闆或從剪貼闆傳回的是裝置相關位圖句柄。下面這段示例代碼将把存在于剪貼闆中的位圖資料顯示到程式的客戶區:

HWND hWnd = GetSafeHwnd(); // 擷取安全視窗句柄

::OpenClipboard(hWnd); // 打開剪貼闆

HANDLE hBitmap = ::GetClipboardData(CF_BITMAP); // 擷取剪貼闆資料句柄

HDC hDC = ::GetDC(hWnd); // 擷取裝置環境句柄

HDC hdcMem = CreateCompatibleDC(hDC); // 建立與裝置相關的記憶體環境

SelectObject(hdcMem, hBitmap); // 選擇對象

SetMapMode(hdcMem, GetMapMode(hDC)); // 設定映射模式

BITMAP bm; // 得到位圖對象

GetObject(hBitmap, sizeof(BITMAP), &bm);

BitBlt(hDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); //位圖複制

::ReleaseDC(hWnd, hDC); // 釋放裝置環境句柄

DeleteDC(hdcMem); // 删除記憶體環境

::CloseClipboard(); // 關閉剪貼闆

多資料項和延遲送出技術

要把資料放入剪貼闆,在打開剪貼闆後一定要調用EmptyClipboard()函數清除目前剪貼闆中的内容,而不可以在原有資料項基礎上追加新的資料項。但是,可以在EmptyClipboard()和CloseClipboard()調用之間多次調用SetClipboardData()函數來放置多個不同格式的資料項。例如:

OpenClipboard(hWnd);

EmptyClipboardData();

SetClipboardData(CF_TEXT, hGMemText);

SetClipboardData(CF_BITMAP, hBitmap);

CloseClipboard();

這時如果用CF_TEXT或CF_BITMAP等格式标記去調用IsClipboardFormatAvailable()都将傳回TRUE,表明這幾種格式的資料同時存在于剪貼闆中。以不同的格式标記去調用GetClipboardData()函數可以得到相應的資料句柄。

對于多資料項的剪貼闆資料,還可以用CountClipboardFormats()和EnumClipboardFormats()函數得到目前剪貼闆中存在的資料格式數目和具體的資料格式。EnumClipboardFormats()的函數原型為:

UINT EnumClipboardFormats(UINT format);

參數format指定了剪貼闆的資料格式。如果成功執行将傳回format指定的格式的下一個資料格式值,如果format為最後的資料格式值,那麼将傳回0。由此不難寫出處理剪貼闆中所有格式資料項的程式段代碼:

UINT format = 0; // 從第一種格式值開始枚舉

OpenClipboard(hWnd);

while(format = EnumClipboardFormats(format))

{

…… // 對相關格式資料的處理

}

CloseClipboard();

在資料提供程序建立了剪貼闆資料後,一直到有其他程序擷取剪貼闆資料前,這些資料都要占據記憶體空間。如在剪貼闆放置的資料量過大,就會浪費記憶體空間,降低對資源的使用率。為避免這種浪費,可以采取延遲送出(Delayed rendering)技術,即由資料提供程序先建立一個指定資料格式的空(NULL)剪貼闆資料塊,直到有其他程序需要資料或自身程序要終止運作時才真正送出資料。

延遲送出的實作并不複雜,隻需剪貼闆擁有者程序在調用SetClipboardData()将資料句柄參數設定為NULL 即可。延遲送出的擁有者程序需要做的主要工作是對WM_RENDERFORMAT、WM_DESTORYCLIPBOARD和 WM_RENDERALLFORMATS等剪貼闆延遲送出消息的處理。

當另一個程序調用GetClipboardData()函數時,系統将會向延遲送出資料的剪貼闆擁有者程序發送WM_RENDERFORMAT消息。剪貼闆擁有者程序在此消息的響應函數中應使用相應的格式和實際的資料句柄來調用SetClipboardData()函數,但不必再調用OpenClipboard()和EmptyClipboard()去打開和清空剪貼闆了。在設定完資料有也無須調用CloseClipboard()關閉剪貼闆。如果其他程序打開了剪貼闆并且調用EmptyClipboard()函數去清空剪貼闆的内容,接管剪貼闆的擁有權時,系統将向延遲送出的剪貼闆擁有者程序發送WM_DESTROYCLIPBOARD消息,以通知該程序對剪貼闆擁有權的喪失。而失去剪貼闆擁有權的程序在收到該消息後則不會再向剪貼闆送出資料。另外,在延遲送出程序在送出完所有要送出的資料後也會收到此消息。如果延遲送出剪貼闆擁有者程序将要終止,系統将會為其發送一條WM_RENDERALLFORMATS消息,通知其打開并清除剪貼闆内容。在調用 SetClipboardData()設定各資料句柄後關閉剪貼闆。

下面這段代碼将完成對資料的延遲送出,WM_RENDERFORMAT消息響應函數OnRenderFormat()并不會立即執行,當有程序調用GetClipboardData()函數從剪貼闆讀取資料時才會發出該消息。在消息處理函數中完成對資料的送出:

進行延遲送出:

HWND hWnd = GetSafeHwnd(); // 擷取安全視窗句柄

::OpenClipboard(hWnd); // 打開剪貼闆

::EmptyClipboard(); // 清空剪貼闆

::SetClipboardData(CF_TEXT, NULL); // 進行剪貼闆資料的延遲送出

::CloseClipboard(); // 關閉剪貼闆

在WM_RENDERFORMAT消息的響應函數中:

DWORD dwLength = 100; // 要複制的字串長度

HANDLE hGlobalMemory = GlobalAlloc(GHND, dwLength + 1); // 配置設定記憶體塊

LPBYTE lpGlobalMemory = (LPBYTE)GlobalLock(hGlobalMemory); // 鎖定記憶體塊

for (int i = 0; i 〈 dwLength; i++) // 将"*"複制到全局記憶體塊

*lpGlobalMemory++ = '*';

GlobalUnlock(hGlobalMemory); // 鎖定記憶體塊解鎖

::SetClipboardData(CF_TEXT, hGlobalMemory); // 将記憶體中的資料放置到剪貼闆

    DSP和自定義資料格式的使用

Windows系統預定義了三個帶“DSP”字首的資料格式:CF_DSPTEXT、CF_DSPBITMAP和 CF_DSPMETAFILEPICT。這是一些僞标準格式,用于表示在程式中定義的私有剪貼闆資料格式。對于不同的程式,這些格式的規定是不同的,是以這些格式隻針對某一具體程式的不同執行個體才有意義。

為使用DSP資料格式,必須確定程序本身與剪貼闆擁有者程序同屬一個程式。可以調用GetClipboardOwner()函數來擷取剪貼闆擁有者視窗句柄,并調用GetClassName()來擷取視窗類名:

HWND hClipOwner = GetClipboardOwner();

GetClassName(hClipOwner, &ClassName, 255);

如果剪貼闆擁有者視窗類名同本程序的視窗類名一緻,就可以使用帶有DSP字首的剪貼闆資料格式了。

除了使用Windows預定義的剪貼闆資料格式外,也可以在程式中使用自定義的資料格式。對于自定義的資料格式lpszFormat,可以調用RegisterClipboardFormat()函數來登記,并擷取其傳回的格式辨別值:

UINT format = RegisterClipboardFormat(lpszFormat);

對此傳回的格式辨別值的使用與系統預定義的格式辨別是一樣的。可以通過GetClipboardFormatName()函數來擷取自定義格式的ASCII名。

http://apps.hi.baidu.com/share/detail/30391289