0

我一直试图让它工作一段时间,但我似乎无法弄清楚,而且谷歌搜索数小时还没有显示任何有用的结果。

我有一个 RGBA 顺序的 32 位像素数组,我想从它们创建一个与设备无关的位图,并使用SetClipboardData(CF_DIBV5, dib)或类似方法将其放置在剪贴板上(理想情况下,我想保留 alpha 通道)。注册自定义剪贴板类型不是一种选择,因为将其放在剪贴板上的目的是可以将其粘贴到另一个程序中。如果我不必手动将像素数据转换为其他格式(例如平面 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();

最终,我还需要能够扭转这个过程(从剪贴板上获取一个可能透明的位图),但一次只做一件事。

4

1 回答 1

2

您不能传递HBITMAPto SetClipboardData()。它需要一个HGLOBALfromGlobalAlloc()代替。这就是为什么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);
于 2015-06-17T22:42:06.063 回答