剪貼板是 Windows 應用程式間資料交換的核心通道。MFC 封裝 OpenClipboard/EmptyClipboard/SetClipboardData/GetClipboardData 等 API,支援標準格式(文字、繪圖、位圖)與自訂格式。本文系統講解六大操作場景,附完整代碼範例。
if (OpenClipboard()) {
EmptyClipboard(); // 寫入前必須清空
// ... 準備 HGLOBAL 資料 ...
SetClipboardData(format, hGlobal);
CloseClipboard();
}
所有剪貼板操作必須成對呼叫OpenClipboard/CloseClipboard,否則導致其他程式無法存取。
CString strSource = _T("要複製的文字內容");
if (OpenClipboard()) {
EmptyClipboard();
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (strSource.GetLength() + 1) * sizeof(TCHAR));
LPTSTR pBuf = (LPTSTR)GlobalLock(hGlobal);
_tcscpy(pBuf, strSource);
GlobalUnlock(hGlobal);
SetClipboardData(CF_UNICODETEXT, hGlobal); // Unicode 版本
CloseClipboard();
}
CString strFromClipboard;
if (OpenClipboard()) {
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
if (hData) {
LPTSTR pBuf = (LPTSTR)GlobalLock(hData);
strFromClipboard = pBuf;
GlobalUnlock(hData);
}
CloseClipboard();
}
if (OpenClipboard()) {
EmptyClipboard();
CMetaFileDC *pDC = new CMetaFileDC();
pDC->CreateEnhanced(NULL, NULL, NULL, _T("MyMetaFile"));
// 繪圖指令...
// pDC->Rectangle(...); pDC->TextOut(...);
HENHMETAFILE hMF = pDC->CloseEnhanced();
SetClipboardData(CF_ENHMETAFILE, hMF);
CloseClipboard();
delete pDC; // 注意:刪除 DC 不會刪除 metafile handle
}
if (OpenClipboard()) {
HENHMETAFILE hMF = (HENHMETAFILE)GetClipboardData(CF_ENHMETAFILE);
if (hMF) {
CClientDC dc(this);
CRect rect(0, 0, 200, 200);
dc.PlayMetaFile(hMF, rect);
}
CloseClipboard();
}
if (OpenClipboard()) {
EmptyClipboard();
CClientDC cdc(this);
CDC memDC;
memDC.CreateCompatibleDC(&cdc);
CBitmap bmp;
bmp.CreateCompatibleBitmap(&cdc, 200, 200);
CBitmap *pOld = memDC.SelectObject(&bmp);
// 繪製內容
// DrawImage(&memDC, _T("Bitmap"));
memDC.SelectObject(pOld);
SetClipboardData(CF_BITMAP, bmp.Detach()); // 傳遞 HBITMAP
CloseClipboard();
}
if (OpenClipboard()) {
HBITMAP hBmp = (HBITMAP)GetClipboardData(CF_BITMAP);
if (hBmp) {
CBitmap bmp;
bmp.Attach(hBmp);
CClientDC cdc(this);
CDC memDC;
memDC.CreateCompatibleDC(&cdc);
CBitmap *pOld = memDC.SelectObject(&bmp);
BITMAP bmInfo;
bmp.GetBitmap(&bmInfo);
cdc.BitBlt(0, 0, bmInfo.bmWidth, bmInfo.bmHeight, &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(pOld);
bmp.Detach(); // 避免析構時刪除共享 handle
}
CloseClipboard();
}
定義專屬資料結構,跨程式交換強型別資料。
struct MyFormatData {
long val1;
int val2;
};
UINT MY_CUSTOM_FORMAT = RegisterClipboardFormat(_T("MY_CUSTOM_FORMAT"));
MyFormatData data = {100, 200};
if (OpenClipboard()) {
EmptyClipboard();
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(MyFormatData));
MyFormatData *pBuf = (MyFormatData*)GlobalLock(hGlobal);
*pBuf = data;
GlobalUnlock(hGlobal);
SetClipboardData(MY_CUSTOM_FORMAT, hGlobal);
CloseClipboard();
}
MyFormatData data;
if (OpenClipboard()) {
HANDLE hData = GetClipboardData(MY_CUSTOM_FORMAT);
if (hData) {
MyFormatData *pBuf = (MyFormatData*)GlobalLock(hData);
data = *pBuf;
GlobalUnlock(hData);
}
CloseClipboard();
}
將視窗加入剪貼板檢視鏈,接收變更通知。
// 在 OnInitDialog 或 OnCreate 中
SetClipboardViewer(); // 將本視窗加入檢視鏈
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
ON_MESSAGE(WM_DRAWCLIPBOARD, OnClipboardChange)
END_MESSAGE_MAP()
afx_msg LRESULT CMyDlg::OnClipboardChange(WPARAM, LPARAM) {
CTime t = CTime::GetCurrentTime();
SetDlgItemText(IDC_TIME, t.Format(_T("%Y-%m-%d %H:%M:%S")));
DisplayClipboardText(); // 自訂函式顯示內容
// 必須傳遞給下一個檢視者
SendMessage(WM_DRAWCLIPBOARD, 0, 0);
return 0;
}
重要:收到WM_DRAWCLIPBOARD後必須SendMessage轉發給鏈中下一個視窗,否則斷開通知鏈。視窗銷毀時呼叫ChangeClipboardChain移除自身。
// 取得目標視窗句柄 (FindWindow/EnumWindows 等)
HWND hTarget = FindWindow(_T("Notepad"), NULL);
if (hTarget) {
// 模擬 Ctrl+V
PostMessage(hTarget, WM_KEYDOWN, VK_CONTROL, 0);
PostMessage(hTarget, WM_KEYDOWN, 'V', 0);
PostMessage(hTarget, WM_KEYUP, 'V', 0);
PostMessage(hTarget, WM_KEYUP, VK_CONTROL, 0);
}
或使用 SendInput 模擬鍵盤事件,更可靠跨進程注入。
| 常數 | 資料類型 | 說明 |
|---|---|---|
| CF_TEXT | ANSI 字串 | 傳統格式 |
| CF_UNICODETEXT | Unicode 字串 | 現代應用首選 |
| CF_BITMAP | HBITMAP | 裝置相關位圖 |
| CF_DIB | HGLOBAL (BITMAPINFO+資料) | 裝置獨立位圖 |
| CF_ENHMETAFILE | HENHMETAFILE | 增強繪圖檔 |
| CF_HDROP | HDROP | 檔案拖放清單 |
掌握 MFC 剪貼板六大操作——標準格式讀寫、自訂格式強型別交換、變更通知鏈、跨程式自動貼上——即能實作豐富的資料共享功能。關鍵紀律:成對 Open/Close、寫入前 Empty、GlobalAlloc 用 GMEM_DDESHARE、自訂格式用 RegisterClipboardFormat、通知鏈必須轉發。遵循原則,剪貼板程式碼將穩健且可攜。