【问题标题】:How to copy bitmap to clipboard using the win32 API?如何使用 win32 API 将位图复制到剪贴板?
【发布时间】:2010-12-30 22:42:39
【问题描述】:

如何使用 win32 API 将保存到“.BMP”文件的缓冲区复制到剪贴板?即,我有一个 Windows V3 位图(包括标题)的原始缓冲区,我可以将其 write() 直接复制到一个文件中,并生成一个有效的 .BMP 文件,但我想将其复制到剪贴板。

在 OS X 上,使用纯 C 语言,代码看起来像这样(按预期工作):

#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;
}

我不确定如何使用 win32 API 完成此操作。这是据我所知,但它似乎静默失败(也就是说,该函数返回一个成功的错误代码,但在尝试粘贴时,菜单项被禁用)。

#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;
}

谁能提供一些关于如何解决这个问题的见解?

编辑

根据 Aaron 和 martinr 的建议,我现在将代码修改为以下内容:

#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;
}

但它仍然有相同的结果。我做错了什么?

最终编辑

工作代码:

#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;
}

谢谢,马丁!

【问题讨论】:

  • 感谢您提出这个问题和答案。
  • 如果 SetClipboardData() 失败,最终编辑代码会泄漏 hResult。

标签: c windows winapi bitmap clipboard


【解决方案1】:

我认为 hMem 需要是 LocalAlloc 的返回值,一个 HMEMORY 而不是指针。

编辑

抱歉,需要带有 GMEM_MOVEABLE 的 GlobalAlloc,而不是 LocalAlloc。

编辑

我建议你使用CF_DIB剪贴板数据格式类型。

DIB 与 BMP 相同,只是它没有 BITMAPFILEHEADER,因此复制源字节,除了第一个 sizeof(BITMAPFILEHEADER) 字节。

编辑

来自 OpenClipboard() 文档 (http://msdn.microsoft.com/en-us/library/ms649048(VS.85).aspx):

“如果应用程序在 hwnd 设置为 NULL 的情况下调用 OpenClipboard,EmptyClipboard 会将剪贴板所有者设置为 NULL;这会导致 SetClipboardData 失败。”

你需要设置一个窗口;即使你没有做 WM_RENDERFORMAT 类型的东西。

我在 Windows API 中发现了很多。我本身并没有使用剪贴板 API,但是对于其他 API,我通常发现创建一个隐藏窗口并将该句柄传递给相关 API 就足以让它保持安静。如果您从 DLL 而不是 EXE 创建窗口,通常会有一些关于问题 的注释;阅读有关 DLL、消息循环和窗口创建的 Microsoft 最新消息。

至于BITMAPINFO,这不是剪贴板想要查看的流的开始:- 您提供给 SetClipboardData 的缓冲区应该在 BITMAPFILEHEADER 停止的位置之后立即开始。

【讨论】:

  • 当试图将数据粘贴到使用 CF_DIB 复制的 Paint 中时,我收到以下两个错误(一个接一个):1.)“获取剪贴板数据时出错!” 2.)“内存或资源不足,无法完成操作。请关闭一些程序,然后重试。”知道这是为什么吗?
  • 此外,文档指出 CF_DIB 是“包含 BITMAPINFO 结构后跟位图位的内存对象”,这似乎与您的描述相矛盾。
  • 哦,抱歉,我发现我将 BITMAPFILEHEADER 误读为 BITMAPINFO。你是对的。
【解决方案2】:

您需要将 HANDLE 传递给 SetClipboard()(即 - 使用 GlobalAlloc() 分配的内存),而不是将直接指针传递给您的位图。

【讨论】:

  • 我已经更新了这个例子,但它似乎仍然不起作用。你介意再看一遍吗?
【解决方案3】:

回声亚伦和马丁。示例here,关键部分:

    // Allocate a global memory object for the text. 

    hglbCopy = GlobalAlloc(GMEM_MOVEABLE, 
        (cch + 1) * sizeof(TCHAR)); 
    if (hglbCopy == NULL) 
    { 
        CloseClipboard(); 
        return FALSE; 
    } 

    // Lock the handle and copy the text to the buffer. 

    lptstrCopy = GlobalLock(hglbCopy); 
    memcpy(lptstrCopy, &pbox->atchLabel[ich1], 
        cch * sizeof(TCHAR)); 
    lptstrCopy[cch] = (TCHAR) 0;    // null character 
    GlobalUnlock(hglbCopy); 

    // Place the handle on the clipboard. 

    SetClipboardData(CF_TEXT, hglbCopy); 

【讨论】:

  • 该代码暗示您不需要将 GlobalFree() 缓冲区传递给 SetClipboardData(),是这样吗?
  • 我不是 100% 了解剪贴板架构,但我认为是的:你不调用 GlobalFree。我认为剪贴板不会复制数据,而是在内部使用您 GlobalAlloc'ed 的缓冲区。
  • 我尝试在我的代码中使用 GlobalFree(),它似乎按预期工作。这实际上对我的功能产生了很大影响——泄漏“.BMP”文件是一个很大的内存泄漏。
  • 哈!好吧,一半的互联网是错误的。 “剪贴板现在负责 GlobalFree 数据,而不是我们自己。” -- netez.com/2xExplorer/shellFAQ/adv_clip.html 感谢您的跟进。
猜你喜欢
  • 1970-01-01
  • 2011-04-19
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 2019-10-30
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
相关资源
最近更新 更多