JD80121
JD80121

Reputation: 609

WinAPI: Correctly copying a HBITMAP to the clipboard

I have some difficulties trying to copy a HBITMAP to the clipboard. My HBITMAP is created from a COLORREF array and I am able to display it correctly. Here is how it is created:

COLORREF* colors = new COLORREF[imageSize[0] * imageSize[1]];

for (int i = 0; i < imageSize[1]; i++) {
    for (int j = 0; j < imageSize[0]; j++) {
        colors[imageSize[0] * i + j] = RGB(/* ... */);
    }
}

// Create bitmap
HBITMAP hBitmap = CreateBitmap(imageSize[0], imageSize[1], 1, 32, (void*)colors);

delete[] colors;

In order to copy my bitmap to the clipboard, I use this small piece of code:

OpenClipboard(hWnd);
EmptyClipboard();

SetClipboardData(CF_BITMAP, hBitmap);

CloseClipboard();

When I execute my app, I am able to copy the bitmap and paste it somewhere, for example in MS Paint. But if I try to copy it a second time, the clipboard content can't be pasted anymore unless the first piece of code above is executed again.

In the MSDN documentation, it is said that

If SetClipboardData succeeds, the system owns the object identified by the hMem parameter.

I don't understand exactly what this means, but I guess it is the source of my problem. I found an example of a function which does what I want here, but it does not seem to use the same kind of variables. Another example, using strings this time, can be found here.

I am not too sure how to translate this last example to my case. Could you point me in the right direction?

Upvotes: 5

Views: 3392

Answers (2)

Destiny
Destiny

Reputation: 506

// You can't pass hBitmap to SetClipboardData directly
OpenClipboard(NULL)
HBITMAP hBitmap = getBit(); // From somewhere

DIBSECTION ds;
::GetObject(hBitmap, sizeof(DIBSECTION), &ds);
//make sure compression is BI_RGB
ds.dsBmih.biCompression = BI_RGB;
HDC hdc = ::GetDC(NULL);
HBITMAP hbitmap_ddb = ::CreateDIBitmap(
    hdc, &ds.dsBmih, CBM_INIT, ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
::ReleaseDC(NULL, hdc);

EmptyClipboard();
SetClipboardData(CF_BITMAP, hbitmap_ddb);
CloseClipboard();

Upvotes: 2

JD80121
JD80121

Reputation: 609

A comment that was deleted helped me find the answer. I actually have to copy my HBITMAP to another HBITMAP before calling SetClipboardData. This way, the copied bitmap can be sent to the clipboard, and the original bitmap is kept for later.

To copy the bitmap, I used the code that can be found in Copying a Bitmap to another Bitmap. In my code, it looks like this:

// Create a new bitmap
HBITMAP hBitmap_copy = CreateBitmap(imageSize[0], imageSize[1], 1, 32, NULL);

// Copy the source bitmap to the new one    
HDC srcDC = CreateCompatibleDC(GetDC(NULL));
HDC newDC = CreateCompatibleDC(GetDC(NULL));

HBITMAP srcBitmap = (HBITMAP)SelectObject(srcDC, hBitmap);
HBITMAP newBitmap = (HBITMAP)SelectObject(newDC, hBitmap_copy);

BitBlt(newDC, 0, 0, imageSize[0], imageSize[1], srcDC, 0, 0, SRCCOPY);

SelectObject(srcDC, srcBitmap);
SelectObject(newDC, newBitmap);

DeleteDC(srcDC);
DeleteDC(newDC);

// hBitmap_copy can now be copied to the clipboard
OpenClipboard(hWnd);
EmptyClipboard();   

SetClipboardData(CF_BITMAP, hBitmap_copy);

CloseClipboard();

I can now copy the displayed bitmap as many times as I want!

Upvotes: 3

Related Questions