How do I copy a buffer that would save to a ".BMP" file to the clipboard using the win32 API? I.e., I have a raw buffer of a Windows V3 Bitmap (including the header) that I can literally write()
to a file and will result in a valid .BMP file, but I want to copy it to the clipboard instead.
On OS X, in plain C, the code would look something like this (which works as intended):
#include <ApplicationServices/ApplicationServices.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
PasteboardRef clipboard;
CFDataRef data;
if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
return PASTE_OPEN_ERROR;
}
if (PasteboardClear(clipboard) != noErr) return PASTE_CLEAR_ERROR;
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bitmapBuffer, buflen,
kCFAllocatorNull);
if (data == NULL) {
CFRelease(clipboard);
return PASTE_DATA_ERROR;
}
if (PasteboardPutItemFlavor(clipboard, 42, kUTTypeBMP, data, 0) != noErr) {
CFRelease(data);
CFRelease(clipboard);
return PASTE_PASTE_ERROR;
}
CFRelease(data);
CFRelease(clipboard);
return PASTE_WE_DID_IT_YAY;
}
I am unsure how to accomplish this with the win32 API. This is as far as I've gotten, but it seems to silently fail (that is, the function returns with a successful error code, but when attempting to paste, the menu item is disabled).
#include <windows/windows.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
if (!OpenClipboard(NULL)) return PASTE_OPEN_ERROR;
if (!EmptyClipboard()) return PASTE_CLEAR_ERROR;
if (SetClipboardData(CF_DSPBITMAP, bitmapBuffer) == NULL) {
CloseClipboard();
return PASTE_PASTE_ERROR;
}
CloseClipboard();
return PASTE_WE_DID_IT_YAY;
}
Could anyone provide some insight as to how to fix this?
Per Aaron and martinr's suggestions, I've now modified the code to the following:
#include <windows/windows.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
HGLOBAL hResult;
if (!OpenClipboard(NULL)) return PASTE_OPEN_ERROR;
if (!EmptyClipboard()) return PASTE_CLEAR_ERROR;
hResult = GlobalAlloc(GMEM_MOVEABLE, buflen);
if (hResult == NULL) return PASTE_DATA_ERROR;
memcpy(GlobalLock(hResult), bitmapBuffer, buflen);
GlobalUnlock(hResult);
if (SetClipboardData(CF_DSPBITMAP, hResult) == NULL) {
CloseClipboard();
return PASTE_PASTE_ERROR;
}
CloseClipboard();
return PASTE_WE_DID_IT_YAY;
}
But it still has the same result. What am I doing wrong?
The working code:
#include <windows/windows.h>
int copyBitmapToClipboard(char *bitmapBuffer, size_t buflen)
{
HGLOBAL hResult;
if (!OpenClipboard(NULL)) return PASTE_OPEN_ERROR;
if (!EmptyClipboard()) return PASTE_CLEAR_ERROR;
buflen -= sizeof(BITMAPFILEHEADER);
hResult = GlobalAlloc(GMEM_MOVEABLE, buflen);
if (hResult == NULL) return PASTE_DATA_ERROR;
memcpy(GlobalLock(hResult), bitmapBuffer + sizeof(BITMAPFILEHEADER), buflen);
GlobalUnlock(hResult);
if (SetClipboardData(CF_DIB, hResult) == NULL) {
CloseClipboard();
return PASTE_PASTE_ERROR;
}
CloseClipboard();
GlobalFree(hResult);
return PASTE_WE_DID_IT_YAY;
}
Thanks, martinr!
I think the hMem needs to be a return value from LocalAlloc, an HMEMORY rather than a pointer.
EDIT
Sorry yes, GlobalAlloc with GMEM_MOVEABLE is required, not LocalAlloc.
EDIT
I suggest you use CF_DIB
clipboard data format type.
DIB is the same as BMP except it is without the BITMAPFILEHEADER
, so copy the source bytes except for the first sizeof(BITMAPFILEHEADER)
bytes.
EDIT
From OpenClipboard() documentation (http://msdn.microsoft.com/en-us/library/ms649048(VS.85).aspx):
"If an application calls OpenClipboard with hwnd set to NULL, EmptyClipboard sets the clipboard owner to NULL; this causes SetClipboardData to fail."
You need to set up a window; even if you're not doing WM_RENDERFORMAT type stuff.
I found this a lot with Windows APIs. I haven't used the Clipboard APIs per se but with other APIs I usually found that creating a hidden window and passing that handle to the relevant API was enough to keep it quiet. There's usually some notes on issues to do with this if you're creating a window from a DLL rather than an EXE; read whatever is the latest Microsoft word about DLLs, message loops and window creation.
As regardsBITMAPINFO
, that's not the start of the stream the clipboard wants to see :- the buffer you give to SetClipboardData should start right after where the BITMAPFILEHEADER stops.
You need to pass a HANDLE to SetClipboard() (that is - memory allocated with GlobalAlloc()) rather than passing a straight pointer to your bitmap.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With