如何仅使用直接 WinAPI 将独立于设备的位图放入 Windows 剪贴板?(无 MFC 或其他包装器)
How can I put a device-independent bitmap into the Windows clipboard using just direct WinAPI? (No MFC or other wrappers)
我一直在尝试让它工作一段时间,但我似乎无法弄清楚,几个小时的谷歌搜索还没有发现任何有用的结果。
我有一个 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();
最终,我还需要能够反转该过程(从剪贴板中获取可能透明的位图),但一次只能做一件事。
不能将HBITMAP
传递给SetClipboardData()
。 它需要GlobalAlloc()
的HGLOBAL
。 这就是为什么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);
相关文章:
- C++剪贴板队列粘贴随机结果?
- 将缓冲区复制到剪贴板 [换行错误]
- wxWidgets mac剪贴板在3.1.3上坏了?
- 获取剪贴板数据(CF_HDROP)在剪切和粘贴中失败
- 在 UWP 中找不到剪贴板
- 剪贴板数据上使用的全局大小函数会导致错误
- 将矢量复制到剪贴板
- 通过EventFilter到剪贴板获取QlineEdit的某些属性
- 如何使用OlesetClipboard将我的COM对象放置在剪贴板上时解决问题
- 使用 WIC 阅读剪贴板
- 文本到剪贴板
- 在QT应用程序和Windows Explorer之间剪切和粘贴剪贴板交换
- Linux/C++ 将字符串复制到剪贴板
- 从表视图复制到剪贴板
- 在可以选择剪贴板副本时,如何禁用Ccombobox
- 无法将 HTML 格式的 unicode(wchar_t 使用)复制到剪贴板
- 分配空终止符时Windows剪贴板函数崩溃
- 我是否错误地使用了Windows剪贴板?
- 如何仅使用直接 WinAPI 将独立于设备的位图放入 Windows 剪贴板?(无 MFC 或其他包装器)
- 如何从Windows剪贴板中读取位图