Windows 剪切板API詳解


 剪切、復制、粘貼都是我們在操作電腦的時候經常會用到的功能。但是你知道當我們剪切或者復制的 時候,數據會保存到什么地方嗎?當我們粘貼的時候,又是從什么地方將數據輸出出來呢?這都源自於系統中給我們提供了一個暫存數據的存儲區域,我們稱之為剪 切板,當新的內容送到剪切板后,新的內容將會覆蓋掉舊的內容,即剪切板只能保存一份內容。因為剪切板是在內存當中,所以,電腦關閉或者是重啟以后,存在剪 切板中的內容將會丟失掉。

除了我們人工的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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM