C++Builder TClipboard 完全指南:剪貼板數據傳輸、格式協商、異常安全與跨進程互操作
TClipboard 封裝 Windows 剪貼板 API,提供格式協商、引用計數管理、多格式並存、組件/流/文本/圖像統一介面的高層抽象。全域單例 Clipboard(需 #include <Clipbrd.hpp>)即開箱即用。本文系統講解核心屬性、數據讀寫模式、自定義格式註冊、異常安全 RAII 封裝、跨進程大數據傳輸與常見陷阱。
類結構與單例
TObject
└── TPersistent
└── TClipboard // 無公共構造函數,僅全域單例
// 訪問
#include <Clipbrd.hpp>
TClipboard* Clipboard(); // 進程單例,引用計數管理
核心屬性
| 屬性 | 類型 | 說明 |
AsText | AnsiString | 純文本讀寫(CF_TEXT/CF_UNICODETEXT 自動轉換) |
FormatCount | int | 當前剪貼板格式數量(只讀) |
Formats[int Index] | Word | 格式 ID 陣列(0 基),含系統預定義與自定義格式 |
核心方法
| 方法 | 簽名 | 用途 |
Open/Close | void __fastcall Open/Close() | 鎖定/釋放剪貼板,支援嵌套計數 |
Clear | void __fastcall Clear() | 清空剪貼板(寫入前自動調用) |
HasFormat | bool __fastcall HasFormat(Word fmt) | 檢查指定格式是否可用 |
GetAsHandle | int __fastcall GetAsHandle(Word fmt) | 返回格式數據的 HGLOBAL(需 Open 狀態有效) |
SetAsHandle | void __fastcall SetAsHandle(Word fmt, int h) | 以 HGLOBAL 寫入數據(所有權轉移給剪貼板) |
GetTextBuf/SetTextBuf | int GetTextBuf(char*,int) / void SetTextBuf(char*) | 緩衝區文本讀寫(低層) |
Assign | virtual void Assign(TPersistent* src) | 通用讀寫:
Clipboard->Assign(bitmap) 寫入
bitmap->Assign(Clipboard) 讀出 |
GetComponent/SetComponent | TComponent* GetComponent(Owner,Parent) / void SetComponent(Comp) | 組件樹序列化(需 RegisterClasses) |
標準格式常數
CF_TEXT = 1; // ANSI 文本
CF_BITMAP = 2; // HBITMAP
CF_METAFILEPICT = 3; // HANDLE to METAFILEPICT
CF_SYLK = 4; // Symbolic Link
CF_DIF = 5; // Data Interchange Format
CF_TIFF = 6; // TIFF 圖像
CF_OEMTEXT = 7; // OEM 文本
CF_DIB = 8; // 設備無關位圖
CF_PALETTE = 9; // 調色盤
CF_PENDATA = 10; // 筆跡數據
CF_RIFF = 11; // RIFF 音頻
CF_WAVE = 12; // WAVE 音頻
CF_UNICODETEXT = 13; // Unicode 文本 (WinNT+)
CF_ENHMETAFILE = 14; // 增強圖元檔
CF_HDROP = 15; // 檔案拖放列表
CF_LOCALE = 16; // 語言環境
CF_MAX = 17; // 系統保留上限
// 自定義格式:RegisterClipboardFormat("MyApp/CustomData") → 返回 > CF_MAX 的 ID
異常安全 RAII 封裝
class ScopedClipboard {
bool opened;
public:
ScopedClipboard() : opened(false) {
Clipboard()->Open();
opened = true;
}
~ScopedClipboard() {
if (opened) Clipboard()->Close();
}
// 禁止拷貝
ScopedClipboard(const ScopedClipboard&) = delete;
ScopedClipboard& operator=(const ScopedClipboard&) = delete;
};
// 使用範例:寫入多格式
{
ScopedClipboard cb;
Clipboard()->Clear();
Clipboard()->AsText = "Plain text";
Clipboard()->Assign(myBitmap); // 同時寫入 CF_BITMAP + CF_DIB
// 離開作用域自動 Close
}
自定義格式註冊與大數據傳輸
// 註冊(進程全域唯一)
static UINT MY_FORMAT = 0;
if (MY_FORMAT == 0)
MY_FORMAT = RegisterClipboardFormat(L"MyApp/ObjectData_v1");
// 寫入大數據(> 幾 MB 建議用 HGLOBAL + 共享內存)
{
ScopedClipboard cb;
Clipboard()->Clear();
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dataSize);
void* ptr = GlobalLock(hMem);
memcpy(ptr, data, dataSize);
GlobalUnlock(hMem);
Clipboard()->SetAsHandle(MY_FORMAT, (int)hMem); // 所有權轉移
// 若需同時提供文本預覽
Clipboard()->AsText = "[MyApp Object Data]";
}
// 讀取
{
ScopedClipboard cb;
if (Clipboard()->HasFormat(MY_FORMAT)) {
HGLOBAL hMem = (HGLOBAL)Clipboard()->GetAsHandle(MY_FORMAT);
void* ptr = GlobalLock(hMem);
// 處理數據...
GlobalUnlock(hMem);
// 注意:不要 GlobalFree,剪貼板關閉時系統自動釋放
}
}
跨進程大數據最佳實踐
- 共享內存 + 格式協商:寫入方
GlobalAlloc(GMEM_DDESHARE),讀取方 GetAsHandle 獲取同一 HGLOBAL,零拷貝。
- 檔案落地備援:超大數據(> 100MB)寫入臨時檔,剪貼板僅放
CF_HDROP 或自定義路徑字串。
- 版本化格式名:
MyApp/Type_v2,讀取端按版本解析,實現向前相容。
- 異步通知:
AddClipboardFormatListener 監聽變更,避免輪詢。
常見陷阱
| 現象 | 原因 | 對策 |
| Open 失敗/超時 | 其他進程長期持有剪貼板 | 重試機制;UI 執行緒勿長時間 Open |
| GetAsHandle 返回句柄無效 | 在 Close 後使用句柄 | 必須在 ScopedClipboard 作用域內使用 |
| SetAsHandle 後數據損壞 | 寫入方過早 GlobalFree | 所有權轉移給剪貼板,切勿自行釋放 |
| 組件讀取 EClassNotFound | 未 RegisterClasses | 啟動時 RegisterClasses([__classid(TMyComp)]) |
| Unicode 文本亂碼 | 舊版 ANSI 應用讀 CF_UNICODETEXT | 優先用 AsText(內部自動轉碼) |
TClipboard 以 Assign 多態介面統一了「文本/圖像/組件/自定義二進制」的讀寫模式,配合 Open/Close 引用計數鎖定與 HGLOBAL 共享內存,構建了高效、安全的跨進程數據交換通道。