剪切、復制、粘貼都是我們在操作電腦的時候經常會用到的功能。但是你知道當我們剪切或者復制的 時候,數據會保存到什么地方嗎?當我們粘貼的時候,又是從什么地方將數據輸出出來呢?這都源自於系統中給我們提供了一個暫存數據的存儲區域,我們稱之為剪 切板,當新的內容送到剪切板后,新的內容將會覆蓋掉舊的內容,即剪切板只能保存一份內容。因為剪切板是在內存當中,所以,電腦關閉或者是重啟以后,存在剪 切板中的內容將會丟失掉。
除了我們人工的ctrl+C或ctrl+V進行對剪切板的操作以外,我們同時可以使用Windows給我們提供的API來讓程序來進行剪切板的復制及粘貼。本篇文章以Visual Studio 2012為編程環境,使用C語言來演示程序操作剪切板的詳細教程。
當我們想往剪切板中寫入數據時,我們首先需要做的就是打開剪切板:
Bool OpenClipboard(HWND hWndNewOwner);
hWndNewOwner參數指定關聯到打開剪切板的窗口句柄,傳入NULL表示關聯到當前任務。每次只允許一個進程打開並訪問。當打開后,操作完剪切板以后,就必須要關閉剪切板,因為剪切板每次只能允許一個進程對其進行訪問。如果打開剪切板后不關閉,除非程序結束,否則其他進程就無法使用剪切板。
當我們打開剪切板后,需要做的操作便是清空剪切板,在寫入剪切板數據之前,我們必須得先清空剪切板,得到剪切板的操控權:
Bool EmptyClipboard(void)
當完成以上兩個操作以后,我們開始為剪切板分配內存:
HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
第一個參數為分配內存屬性,可以是以下內存屬性:
GMEM_FIXED | 分配一塊固定的內存去續,不允許系統移動,這時返回值是一個指針。 |
GMEM_MOVEABLE | 分配一塊可移動的內存區域,實際上內存塊在物理內存中是不可移動的,這里的可移動指的是在應用程序 的默認邏輯堆內可以移動。返回值是內存對象的句柄。可以通過調用GlobalLock()函數講一個句柄轉化為 一個指針,這個標志不能和GMEM_FIXED同時使用。 |
GMEM_ZEROINT | 初始化內存對象為全0,如果不用這個標志,內存對象將為不確定的內 |
GHND | GMEM_MOVEABLE和GMEM_ZEROINT聯合使用,即可移動同時初始化為0 |
GPTR | GMEM_FIXED和GMEM_ZEROINT同時使用,即不可移動同時初始化為0 |
第二個參數為分配的大小。如果成功則指向該內存,如果失敗則返回NULL。
如果我們分配內存的參數一的內存屬性為GMEM_FIXED或GPTR,獲得的便是一個指向內存區域的指針,不需要進行鎖定內存及解鎖內存的操作,直接使用memcpy函數將數據復制到分配的內存區域中即可,如果內存屬性為GMEM_MOVEABLE或GHND,獲得的便是一塊內存區域,我們需要將內存區域轉化為一個指針:
LPVOID GlobalLock(HGLOBAL hMem);
如果分配內存時分配的內存屬性為GMEM_MOVEABLE或GHND,那么就需要鎖定內存,鎖定由GlobalAlloc分配的內存,並將內存對象的鎖定計數器+1。如果該函數成功,則返回內存區域的起始地址指針,失敗則返回NULL,GlobalLock會將計數器加1,而GlobalUnlock函數將使計數器減1。對於每次GlobalLock的每一次調用,都必須相應的調用一次GlobalUnlock函數。否則被鎖定的內存空間不能夠被移動或釋放。直到計數器為0時被鎖定的內存快才會被移動或者是釋放。(更詳細的解釋請參考MSDN文檔:GlobalLock function(Windows))
如果執行成功,返回的便是內存區域的起始地址指針,我們可以使用memcpy將數據復制到該片內存區域,當執行完畢復制以后,我們需要解除鎖定的內存:
BOOL GlobalUnlock(HGLOBAL hMem);
參數1為使用分配內存返回的內存區域。
當內存分配完畢后,便可以將數據寫入剪切板了。
HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);
參數1為剪切板的數據格式,可以是以下格式:
CF_TEXT | 以NULL結尾的ANSI字符集字符串。它在每行末尾包含一個carriage return和linefeed字符,這是最簡單的剪切板數據格式 |
CF_OEMTEXT | 含有文字數據(與CF_TEXT類似)的內存塊。但是它使用的是OEM字符集。 |
CF_UNICODETEXT | 含有Unicode文字的內存快。與CF_TEXT類似,它在每一行的末尾包含一個carriage return和linefeed字符,以及一個NULL字符(兩個0字節)以表示數據結束。CF_UNICODETEXT針對UNICONDE格式 |
CF_SYLK | 包含Microsoft 「符號連結」數據格式的整體內存塊。這種格式用在Microsoft的Multiplan、Chart和Excel程序之間交換數據,它是一種ASCII碼格式。 |
CF_DIF | 包含數據交換格式(DIF)之數據的整體內存塊。用於把數據送到VisiCalc電子表格程序中。這也是一種ASCII碼格式 |
CF_BITMAP | 與設備相關的位圖格式。位圖是通過位圖句柄傳送給剪貼簿的。 |
CF_DIB | 定義一個設備無關位圖的內存塊。 |
CF_PALETTE | 調色盤句柄。 |
CF_METAFILEPICT | 以舊的metafile格式存放的「圖片」。 |
CF_ENHMETAFILE | 增強型metafile(32位Windows支持的)句柄。 |
CF_PENDATA | 與Windows的筆式輸入擴充功能聯合使用 |
CF_WAVE | 聲音(波形)文件。 |
CF_RIFF | 使用資源交換文件格式(Resource Interchange File Format)的多媒體數據。 |
CF_HDROP | 與拖放服務相關的文件列表。 |
該數據表使用谷歌自動翻譯完成,如果英文較好,可查看MSDN文檔:Standard Clipboard Formats(Windows)
參數2為分配的內存區域。如果執行成功,則返回數據句柄,否則返回NULL。
當完成對剪切板的操作以后,我們還需要關閉剪切板:
Bool CloseClipboard(void);
直接調用該函數便可關閉剪切板,只有執行此函數后,其他進程才能使用剪切板,關閉剪切板后,當前進程就不能寫入數據。
如果我們想獲取剪切板中數據,可以使用GetClipboardData函數:
HANDLE GetClipboardData(UINT uFormat);
參數1為剪切板的數據格式,參見:剪切板的數據格式
如果執行成功,則返回數據句柄,否則返回NULL。
下面是代碼例程:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <Windows.h> int main(int argc, char * argv[]) { HGLOBAL hMemory; LPTSTR lpMemory; char * content = "藍雨麥郎版權所有"; // 待寫入數據 int contentSize = strlen(content) + 1; if(!OpenClipboard(NULL)) // 打開剪切板,打開后,其他進程無法訪問 { puts("剪切板打開失敗"); return 1; } if(!EmptyClipboard()) // 清空剪切板,寫入之前,必須先清空剪切板 { puts("清空剪切板失敗"); CloseClipboard(); return 1; } if((hMemory = GlobalAlloc(GMEM_MOVEABLE, contentSize)) == NULL) // 對剪切板分配內存 { puts("內存賦值錯誤!!!"); CloseClipboard(); return 1; } if((lpMemory = (LPTSTR)GlobalLock(hMemory)) == NULL) // 將內存區域鎖定 { puts("鎖定內存錯誤!!!"); CloseClipboard(); return 1; } memcpy_s(lpMemory, contentSize, content, contentSize); // 將數據復制進入內存區域 GlobalUnlock(hMemory); // 解除內存鎖定 if(SetClipboardData(CF_TEXT, hMemory) == NULL) { puts("設置剪切板數據失敗!!!"); CloseClipboard(); return 1; } system("pause"); return 0; }