You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将MFC中CBitmap剪贴板存储从OpenClipboard切换到COleDataSource

问题描述

我有一个MFC应用程序,原本通过OpenClipboard()/SetClipboardData()/CloseClipboard()将CBitmap存储到剪贴板,代码如下:

if(OpenClipboard())
{
    if(EmptyClipboard())
    {
        SetClipboardData(CF_BITMAP, pBitmap->m_hObject);
        CloseClipboard();
    }
}
// Error reported omitted for brevity.

这段代码运行正常,但应用程序其他地方使用COleDataSource存储自定义数据到剪贴板:

COleDataSource* pOleSource = new COleDataSource;
pOleSource->CacheGlobalData(uiRiggerFormat, hMem);
pOleSource->SetClipboard();

这部分也能正常工作。

我希望改用COleDataSource存储CBitmap,这样就能同时把CBitmap和自定义数据放到剪贴板(接收端可选择所需格式)。

尝试直接将CBitmap的hObject传入COleDataSource::CacheGlobalData,但粘贴时崩溃,剪贴板对象大小为0:

COleDataSource* pOleSource = new COleDataSource;
pOleSource->CacheGlobalData(CF_BITMAP, pBitmap->m_hObject);
pOleSource->SetClipboard();
// FYI, I do not free pBitmap.

我猜测问题在于pBitmap(未释放)不在全局内存中,但对此了解有限。

之后尝试将pBitmap转移到全局内存:

BITMAP bmpInfo;
pBitmap->GetBitmap(&bmpInfo);
int size = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
BYTE* pBits = new BYTE[size];
pBitmap->GetBitmapBits(size, pBits);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, size);
LPVOID pMem = GlobalLock(hMem);
memcpy(pMem, pBits, size);

// Store it.
COleDataSource* pOleSource = new COleDataSource;
ASSERT(pOleSource);
pOleSource->CacheGlobalData(CF_BITMAP, hMem);
pOleSource->SetClipboard();

此时粘贴不再崩溃,但图片无法显示,我猜测是因为计算的大小仅为位图数据,而非剪贴板所需的完整位图结构。

更新
我找到了可行的代码(感谢Igor Tandetnik的建议):

tagSTGMEDIUM* pStorageMedium = new tagSTGMEDIUM;
memset(pStorageMedium, 0, sizeof(tagSTGMEDIUM));
pStorageMedium->tymed = TYMED_GDI;
pStorageMedium->hBitmap = HBITMAP(*pDestBitmap);
COleDataSource* pOleSource = new COleDataSource;
pOleSource->CacheData(CF_BITMAP, pStorageMedium);
pOleSource->SetClipboard();
delete pStorageMedium;

但创建位图时使用的CDC指针pDC会导致内存泄漏:

CClientDC cdc(this);
CDC* pDc = new CDC();
pDc->CreateCompatibleDC(&cdc);
pBitmap->CreateCompatibleBitmap(&cdc, iBitmapWidth, iBitmapHeight);
const CBitmap* pOldDestBitmap = pDc->SelectObject(pBitmap);
CDC dcBitmap;
dcBitmap.CreateCompatibleDC(pDc);
const CBitmap* pOldBitmap = dcBitmap.SelectObject(&bmpBitmap);
pDc->BitBlt(0, 0, iBitmapWidth, iBitmapHeight, &dcBitmap, 0, 0, SRCCOPY);

如果删除pDC避免泄漏,剪贴板中的位图就无法粘贴,请问如何解决这个内存泄漏问题?

解决方案

一、正确使用COleDataSource存储CBitmap到剪贴板

你找到的CacheData结合STGMEDIUM的方式是正确的,因为CF_BITMAP对应的剪贴板数据是GDI位图句柄,而非全局内存块,所以不能用CacheGlobalData(该方法适用于基于全局内存的格式,比如CF_TEXT)。

使用STGMEDIUM传递HBITMAP时,需注意:

  • 设置tymed = TYMED_GDI,标识这是GDI对象
  • 将hBitmap赋值为CBitmap的句柄
  • COleDataSource会接管这个位图句柄的所有权,所以不需要自行释放pDestBitmap的句柄(除非后续还要使用它)

二、解决CDC的内存泄漏问题

内存泄漏的核心原因是:创建位图后未恢复DC中原来的位图对象,导致DC持有原位图引用,既造成泄漏,又让当前pBitmap无法被剪贴板正确接管。修复步骤如下:

1. 恢复DC中的原有位图

完成BitBlt操作后,必须将DC中原来的位图选回,修改代码如下:

CClientDC cdc(this);
CDC* pDc = new CDC();
pDc->CreateCompatibleDC(&cdc);
pBitmap->CreateCompatibleBitmap(&cdc, iBitmapWidth, iBitmapHeight);
const CBitmap* pOldDestBitmap = pDc->SelectObject(pBitmap);
CDC dcBitmap;
dcBitmap.CreateCompatibleDC(pDc);
const CBitmap* pOldBitmap = dcBitmap.SelectObject(&bmpBitmap);

// 执行绘图操作
pDc->BitBlt(0, 0, iBitmapWidth, iBitmapHeight, &dcBitmap, 0, 0, SRCCOPY);

// 关键:恢复DC中的原有位图,释放对当前位图的引用
dcBitmap.SelectObject(pOldBitmap);
pDc->SelectObject(pOldDestBitmap);

// 现在可以安全释放pDC
delete pDc;
pDc = nullptr;

2. 为什么恢复后剪贴板仍能正常工作?

当你把pBitmap的句柄传递给STGMEDIUM后,COleDataSource会在SetClipboard时将该位图的所有权转移给剪贴板系统,即使释放了pDC,剪贴板依然持有该位图的有效句柄,粘贴操作不受影响。

额外优化:避免动态分配CDC

可以直接使用栈上的CDC对象,彻底避免手动管理内存:

CClientDC cdc(this);
CDC dcDest;
dcDest.CreateCompatibleDC(&cdc);
pBitmap->CreateCompatibleBitmap(&cdc, iBitmapWidth, iBitmapHeight);
const CBitmap* pOldDestBitmap = dcDest.SelectObject(pBitmap);

CDC dcBitmap;
dcBitmap.CreateCompatibleDC(&dcDest);
const CBitmap* pOldBitmap = dcBitmap.SelectObject(&bmpBitmap);

dcDest.BitBlt(0, 0, iBitmapWidth, iBitmapHeight, &dcBitmap, 0, 0, SRCCOPY);

// 恢复原有位图
dcBitmap.SelectObject(pOldBitmap);
dcDest.SelectObject(pOldDestBitmap);

这样从根源上避免了内存泄漏问题。

内容的提问来源于stack exchange,提问作者Steve A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 21:45:03