windows API 剪貼簿詳解
原文出處:http://www.cnblogs.com/cnlyml/archive/2013/03/02/2940434.html
剪下、複製、貼上都是我們在操作電腦的時候經常會用到的功能。但是你知道當我們剪下或者複製的 時候,資料會儲存到什麼地方嗎?當我們貼上的時候,又是從什麼地方將資料輸出出來呢?這都源自於系統中給我們提供了一個暫存資料的儲存區域,我們稱之為剪 切板,當新的內容送到剪下板後,新的內容將會覆蓋掉舊的內容,即剪下板只能儲存一份內容。因為剪下板是在記憶體當中,所以,電腦關閉或者是重啟以後,存在剪 切板中的內容將會丟失掉。
除了我們人工的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; }