1. 程式人生 > >windows API 剪貼簿詳解

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;
}
複製程式碼