在 Windows 程式設計中,將檔案「複製到剪貼板」並非複製檔案內容,而是將檔案路徑清單以 CF_HDROP 格式 放入剪貼板。這樣使用者在總管或其他支援的應用程式中按「貼上」(Ctrl+V) 時,系統會自動執行實際的檔案複製操作。本文提供 C++ Builder 完整實作程式碼,並解析 CF_HDROP 記憶體結構原理。
CF_HDROP 是剪貼板標準格式,用於傳遞檔案路徑清單。其記憶體區塊結構為:
struct DROPFILES {
DWORD pFiles; // 檔案名列表相對結構體起始位置的偏移量 (通常為 20)
POINT pt; // 點擊位置 (通常填 0)
BOOL fNC; // 非用戶區域旗標
BOOL fWide; // 是否為 Unicode (TRUE=雙位元組, FALSE=ANSI)
};
// 緊接著是以雙重 NULL 結尾的檔案路徑字串陣列 (ANSI 或 Unicode)
bool CopyToClipboard(const AnsiString& file) {
if (file.IsEmpty()) return false;
// 1. 計算所需記憶體大小
// DROPFILES 結構 20 位元組 + 檔案路徑長度 + 1 (單一 NULL) + 1 (雙重 NULL 結尾)
int nLen = sizeof(DROPFILES) + file.Length() + 1 + 1;
// 2. 配置可移動、共享、零初始化的全域記憶體
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, nLen);
if (!hMem) return false;
// 3. 鎖定記憶體並填充結構
char* pData = (char*)GlobalLock(hMem);
if (!pData) { GlobalFree(hMem); return false; }
DROPFILES* pDrop = (DROPFILES*)pData;
pDrop->pFiles = sizeof(DROPFILES); // 偏移量指向結構體之後
pDrop->pt.x = 0;
pDrop->pt.y = 0;
pDrop->fNC = FALSE;
pDrop->fWide = FALSE; // 這裡使用 ANSI 版本
// 複製檔案路徑 (包含結尾 NULL)
strcpy(pData + sizeof(DROPFILES), file.c_str());
// 注意:這裡只有一個檔案,所以路徑後已經是雙重 NULL (因 GMEM_ZEROINIT)
GlobalUnlock(hMem);
// 4. 打開剪貼板、清空、設定資料、關閉
bool bSuccess = false;
if (OpenClipboard(NULL)) {
EmptyClipboard();
if (SetClipboardData(CF_HDROP, hMem)) {
hMem = NULL; // 所有權轉移給剪貼板,不再由我們釋放
bSuccess = true;
}
CloseClipboard();
}
// 5. 若設定失敗,釋放記憶體
if (hMem) GlobalFree(hMem);
return bSuccess;
}
// 使用範例
void __fastcall TForm1::Button1Click(TObject *Sender) {
if (CopyToClipboard("C:\\scandisk.log")) {
ShowMessage("檔案路徑已複製到剪貼板,請在總管中按 Ctrl+V 貼上。");
} else {
ShowMessage("複製失敗!");
}
}
GMEM_MOVEABLE 允許記憶體移動 (剪貼板要求);GMEM_DDESHARE 允許跨進程共享 (DDE/剪貼板核心需求);GMEM_ZEROINIT 自動清零,確保字串以雙重 NULL 結尾。DROPFILES 結構 (20 位元組),值固定為 20。fWide = TRUE,並使用 wchar_t 複製路徑 (每個路徑以 L'\0' 結尾,最終雙重 L'\0\0')。Windows 總管完美支援 Unicode 路徑。\0 結尾,最後再加一個 \0。調整 nLen 計算即可。SetClipboardData 成功後,記憶體區塊所有權歸剪貼板,不可 呼叫 GlobalFree。失敗時則由呼叫者負責釋放。若要實現「剪下」 (移動) 語意,需在放下時由目標應用處理。剪貼板資料本身無法區分「複製」與「剪下」。但可結合 CFSTR_PREFERREDDROPEFFECT 格式,在剪貼板同時放入 DROPEFFECT_MOVE (值為 2) 提示目標應用執行移動操作。這需要額外註冊格式並設定資料。
透過構造標準 CF_HDROP 記憶體區塊,C++ Builder 應用程式即可輕松實現「複製檔案路徑到剪貼板」功能,完美融入 Windows Shell 操作流程。掌握其記憶體結構與旗標含義,是進行剪貼板進階開發的基礎。