剪貼板資料格式標準化:**提供方呼叫 OpenClipboard → EmptyClipboard → SetClipboardData (可多次,不同格式) → CloseClipboard**。設定後 GDI 物件所有權移交剪貼板,**必須 Detach 避免雙重釋放**。
CF_BITMAP
void CopyBitmapToClipboard(const CWnd* pWnd, CBitmap& bitmap, CPalette* pPal = NULL) {
if (!::OpenClipboard(pWnd->GetSafeHwnd())) return;
::EmptyClipboard();
if (pPal) {
::SetClipboardData(CF_PALETTE, pPal->GetSafeHandle());
pPal->Detach(); // 所有權移交
}
::SetClipboardData(CF_BITMAP, bitmap.GetSafeHandle());
bitmap.Detach(); // 關鍵!
::CloseClipboard();
}
CBitmap (資源載入、螢幕截圖 CreateCompatibleBitmap)。CF_DIB
void CopyDIBToClipboard(const CWnd* pWnd, HGLOBAL hDIB, CPalette* pPal = NULL) {
if (!::OpenClipboard(pWnd->GetSafeHwnd())) return;
::EmptyClipboard();
if (pPal) {
::SetClipboardData(CF_PALETTE, pPal->GetSafeHandle());
pPal->Detach();
}
::SetClipboardData(CF_DIB, hDIB); // hDIB 包含 BITMAPINFOHEADER + 像素資料
// 注意:此處原文有誤 bitmap.Detach(),應為 hDIB 由 GlobalAlloc 配置,剪貼板接管釋放
// 正確做法:hDIB 為 GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE) 配置,SetClipboardData 後不再手動釋放
::CloseClipboard();
}
DIB 記憶體區塊結構:
// 配置
DWORD dwSize = sizeof(BITMAPINFOHEADER) + nPaletteSize + dwImageSize;
HGLOBAL hDIB = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize);
LPVOID pData = GlobalLock(hDIB);
BITMAPINFOHEADER* pBmi = (BITMAPINFOHEADER*)pData;
// 填充 pBmi...
// 複製像素資料至 pData + sizeof(BITMAPINFOHEADER) + nPaletteSize
GlobalUnlock(hDIB);
CF_DIB (舊)、CF_DIBV5 (新,含色域/ICC 設定檔)。
void CopyWndToClipboard(CWnd* pWnd) {
CBitmap bitmap;
CClientDC dc(pWnd);
CDC memDC; memDC.CreateCompatibleDC(&dc);
CRect rect; pWnd->GetClientRect(&rect);
bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
CBitmap* pOldBmp = memDC.SelectObject(&bitmap);
memDC.BitBlt(0, 0, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);
memDC.SelectObject(pOldBmp);
CopyBitmapToClipboard(pWnd, bitmap); // 復用函式一
}
| 問題 | 說明 | 解決 |
|---|---|---|
忘記 Detach | CBitmap 解構時 DeleteObject,剪貼板持有無效控制代碼 → 貼上失敗/當機 | SetClipboardData 後立即 Detach() |
| DIB 記憶體旗標錯誤 | 未用 GMEM_DDESHARE 或 GMEM_MOVEABLE,剪貼板無法跨行程存取 | GlobalAlloc 必須包含 GMEM_MOVEABLE | GMEM_DDESHARE |
| 調色盤未提供 | 8-bit 位圖貼上到 24-bit 環境顏色錯誤 | 同時 SetClipboardData(CF_PALETTE, ...) |
| 大位圖配置失敗 | GlobalAlloc 失敗返回 NULL | 檢查返回值,考慮分塊或用檔案映射 |
| 非主執行緒操作剪貼板 | OpenClipboard 失敗 | 必須在 UI 執行緒呼叫,或用 OleSetClipboard (OLE 資料物件) |
COleDataSource)
void CopyBitmapOLE(const CWnd* pWnd, CBitmap& bitmap) {
COleDataSource* pDataSrc = new COleDataSource;
// 封裝位圖為 CF_BITMAP/CF_DIB 格式
// 需自行實作 IDataObject::GetData 或用現成封裝
pDataSrc->SetClipboard(); // 非同步,支援延遲渲染
}
優點:支援延遲渲染 (使用者貼上時才生成資料)、多格式並存、拖放支援、非 UI 執行緒安全。