Как я могу поместить независимое от устройства растровое изображение в буфер обмена Windows, используя только прямой WinAPI? (Без MFC или других упаковщиков)

Я пытался заставить это работать некоторое время, но я не могу понять это, и часы Googling еще не показали каких-либо полезных результатов.

У меня есть массив 32-битных пикселей в порядке RGBA, и я хочу создать из них независимое от устройства растровое изображение и поместить его в буфер обмена, используя SetClipboardData(CF_DIBV5, dib) или аналогичный (в идеале, я хочу сохранить альфа-канал). Регистрация пользовательского типа буфера обмена не является вариантом, так как смысл помещать его в буфер обмена так, чтобы его можно было вставить в другую программу. Бонусные баллы, если мне не нужно вручную преобразовывать свои данные пикселей в какой-либо другой формат (например, планарный BGRA).

Мой текущий код выглядит следующим образом (все это внутри функции set_clipboard_img):

if(!OpenClipboard(hwnd)) return;
BITMAPV5HEADER* info = (BITMAPV5HEADER*) GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPV5HEADER));
info->bV5Size = sizeof(BITMAPV5HEADER);
info->bV5Width = img_width;
info->bV5Height = -img_height;
info->bV5Planes = 1; // The docs say this is the only valid value here.
info->bV5BitCount = 32;
info->bV5Compression = BI_BITFIELDS;
info->bV5SizeImage = img_width * img_height * 4;
info->bV5RedMask   = 0xff000000;
info->bV5GreenMask = 0x00ff0000;
info->bV5BlueMask  = 0x0000ff00;
info->bV5AlphaMask = 0x000000ff;
unsigned char* buf;
// One of the sources I found said that I can pass a BITMAPV5HEADER in place of the BITMAPINFO, hence the first reinterpret_cast.
HBITMAP dib = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(info), DIB_RGB_COLORS, reinterpret_cast<void**>(&buf), NULL, 0);
if(dib == NULL) {
CloseClipboard();
return;
}
// img_pixels_ptr is a unsigned char* to the pixels in non-planar RGBA format
std::copy_n(img_pixels_ptr, info->bV5SizeImage, buf);
EmptyClipboard();
auto result = SetClipboardData(CF_DIBV5, dib);
if(result == NULL) {
char str[256];
str[255] = 0;
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, str, 255, NULL);
std::cerr << "Error setting clipboard: " << str << std::endl;
// Here I get "handle not valid". I have no idea _why_ it's not valid, though.
}
CloseClipboard();

В конечном счете, мне также нужно иметь возможность полностью изменить процесс (получить потенциально прозрачное растровое изображение из буфера обмена), но по одной вещи за раз.

0

Решение

Вы не можете передать HBITMAP в SetClipboardData(), Это требует HGLOBAL от GlobalAlloc() вместо. Поэтому SetClipboardData() терпит неудачу с ERROR_INVALID_HANDLE ошибка.

Вы должны поставить свой BITMAPV5HEADER и данные пикселей непосредственно в выделенный HGLOBAL и положить его как есть в буфер обмена, забудьте использовать CreateDIBSection() совсем:

Стандартные форматы буфера обмена

CF_DIBV5
17
Объект памяти, содержащий структуру BITMAPV5HEADER, за которой следуют информация о цветовом пространстве растрового изображения и биты растрового изображения.

Попробуйте что-то вроде этого:

void printErr(const char *msg)
{
char str[256] = {0};
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, str, 255, NULL);
std::cerr << msg << ": " << str << std::endl;
}

...

DWORD size_pixels = img_width * img_height * 4;

HGLOBAL hMem = GlobalAlloc(GHND, sizeof(BITMAPV5HEADER) + size_pixels);
if (!hMem)
{
printErr("Error allocating memory for bitmap data");
return;
}

BITMAPV5HEADER* hdr = (BITMAPV5HEADER*) GlobalLock(hMem);
if (!hdr)
{
printErr("Error accessing memory for bitmap data");
GlobalFree(hMem);
return;
}

hdr->bV5Size = sizeof(BITMAPV5HEADER);
hdr->bV5Width = img_width;
hdr->bV5Height = -img_height;
hdr->bV5Planes = 1;
hdr->bV5BitCount = 32;
hdr->bV5Compression = BI_BITFIELDS;
hdr->bV5SizeImage = size_pixels;
hdr->bV5RedMask   = 0xff000000;
hdr->bV5GreenMask = 0x00ff0000;
hdr->bV5BlueMask  = 0x0000ff00;
hdr->bV5AlphaMask = 0x000000ff;

// img_pixels_ptr is a unsigned char* to the pixels in non-planar RGBA format
CopyMemory(hdr+1, img_pixels_ptr, size_pixels);
GlobalUnlock(hMem);

if (!OpenClipboard(hwnd))
{
printErr("Error opening clipboard");
}
else
{
if (!EmptyClipboard())
printErr("Error emptying clipboard");

else if (!SetClipboardData(CF_DIBV5, hMem))
printErr("Error setting bitmap on clipboard");

else
hMem = NULL; // clipboard now owns the memory

CloseClipboard();
}

if (hMem)
GlobalFree(hMem);
2

Другие решения


По вопросам рекламы [email protected]