Changing CBitmap storage on clipboard from OpenClipboard()/CloseClipboard() to COleDataSource (MFC)

61 Views Asked by At

I have an MFC app that stores a CBitmap on the clipboard using OpenClipboard()/SetClipboardData()/CloseClipboard():

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

It works well, however elsewhere the app stores custom data on the clipboard using COleDataSource:

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

That works well, too.

I would like to change the storage of the CBitmap to use COleDataSource so I can put both the CBitmap and the custom data on the clipboard at the same time (and the app receiving the paste can decide which format it wants).

I tried using COleDataSource like this (passing the CBitmap's hObject), but it crashes on paste. It seems the object's size on the clipboard is 0.

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

I suspect the problem is the pBitmap (which I am not freeing) isn't in global memory. I barely know what that means.

Based on code I found elsewhere, I tried to move the pBitmap to global memory like this:

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();

That no longer crashes on paste, but the image doesn't appear. I suspect that's because the size I am calculating (which is the bitmap bits) isn't the same as the size of the entire bitmap that needs to be copied to the clipboard.

Can you straighten me out? Thanks!

UPDATE

I came across the following code (and see that Igor suggested this approach):

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;

This works! However, the pDC (CDC pointer) I create when creating the bitmap (as shown below; error checking not shown for brevity) results in a memory leak.

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);

If I delete the pDC (to prevent the leak), the bitmap can't be pasted from the clipboard.

What can I do to prevent the leak?

Thanks! (And thanks, Igor Tandetnik!)

0

There are 0 best solutions below