复制 DIB 到剪贴板
帮助将设备无关位图复制到剪贴板。
引言
最近,我需要将 DIB 保存到剪贴板。我尝试了多种方法,持续了四周,但没有一个有效(非常感谢 Chris Losinger 和 Christian Graus 愿意尝试并为我提供指导、答案或两者都有)。 本文介绍了我是如何最终实现目标的,但也恰好属于“不好看但有效”的类别。
试验和磨难
由于我需要这样做的原因与本文的范围关系不大,因此可以肯定的是,我的起点是一个 BITMAPINFOHEADER
结构和一个指向代表实际位图数据的位数组的指针。 该位图是 24 位类型。
起初,我四处尝试用我拥有的信息创建一个位图句柄。 我确信我做得对,但在将该位图移动到剪贴板方面屡遭失败,我(很自然地)认为我一定搞砸了,所以我去寻找帮助,最后使用了 Chris Maunder 的 CDIBSectionLite 类。
Chris 的类包含将我捕获的位图转换为 DIBSection 所需的所有代码,但它缺少将 DIB 移动到剪贴板的代码。 事实证明,这是一个很好的起点。 然而,无论我尝试什么,我都无法将位图移动到剪贴板。
它是如何组合在一起的
在尝试从我的代码(和 CDIBSectionLite)中提取所需功能之后,我拨打了 Microsoft 软件开发人员热线(使用我的 MSDN 订阅中的两个可用免费事件之一)。 微软的人把我引向了一个与编译器一起提供的示例,称为 wincap32。 这个示例程序将捕获所选窗口的内容并将其转换为 DIB,并将其存储在剪贴板上。 完美。
在更改了一两个 #include 语句并更改了一个文件的名称后,我编译了我的代码,运行了生成的程序,然后一拍即合 - DIB 在剪贴板上!
我做了什么
我做的第一件事是向 CDIBSectionLite
类添加了这个函数(记住,不好看但有效
HANDLE CDIBSectionLite::PutOnClipboard() { HANDLE hResult = NULL; if (::OpenClipboard(NULL)) { ::EmptyClipboard(); ::GdiFlush(); // borrowed these variables from the sample app HDIB hDib = NULL; HBITMAP hBitmap = NULL; HPALETTE ghPal = NULL; if (m_hBitmap) { // call the function that converts the bitmap to a DIB hDib = BitmapToDIB(m_hBitmap, ghPal); if (hDib) { // ahhh, the sweet smell of success hResult = ::SetClipboardData(CF_DIB, hDib); if (hResult == NULL) { _ShowLastError(); } } else { MessageBeep(0); } } ::CloseClipboard(); } return hResult; }
从 Microsoft 示例应用程序中,我将以下文件复制到我的项目目录中
DIBUTIL.C DIBUTIL.H DIBAPI.H
然后,我将 DIBUTIL.C 文件重命名为 DIBUTIL.CPP。 之后,我不得不对该文件进行以下更改,以便它可以编译。 示例应用程序编译为 is,但是因为我包含了 stdafx.h,所以编译器在某些类型不匹配时出错,因此需要进行以下更改
Line 30: #include <windows.h> to: #include "stdafx.h" Line 381: lpbi = GlobalLock(hDIB); to: lpbi = (LPSTR)GlobalLock(hDIB); Line 524: lpDIBHdr = GlobalLock(hDIB); to: lpDIBHdr = (LPSTR)GlobalLock(hDIB); Line 608: hPal = GetStockObject(DEFAULT_PALETTE); to: hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
最后,我使用整个应用程序如下
// I needed to construct the BITMAPINFO structure to be passed to the // CDIBSectionLite object would be happy HBITMAP hBitmap; BITMAPINFO bmi; memset(&bmi, 0, sizeof(BITMAPINFO)); // use the BITMAPINFOHEADER structure that we captured bmi.bmiHeader = cb.bih; CDIBSectionLite dib; // use the BITMAPINFO struct that we created above and the bits we captured dib.SetBitmap(&bmi, cb.pBuffer); dib.PutOnClipboard();
最后
它有效。我并不真正感兴趣从我从 Microsoft 示例应用程序中获取的文件中提取我需要的内容,所以我打算将它们全部留在那里。这是一个我留给读者练习。
本文附带的 zip 文件包含修改后的 CDIBSectionLite
类以及修改后的示例应用程序文件,以及整个示例应用程序的原始形式的源代码。需要注意的一件事是,如果编译为 is,示例应用程序项目会创建(或提供)一个名为 DIBAPI.DLL 的 DLL。 该 DLL 包含我直接链接到我的程序中的代码(我不希望它以 DLL 的形式出现)。
由于我捕获的位图的大小,我没有为 CDIBSectionLite
配备将剪贴板上的位图另存为 DIB 以外的任何内容的功能。 但是,请记住,可以在剪贴板上同时保存多个项目,因此您可以同时(使用示例应用程序代码)将位图以 DIB 格式、设备相关格式和图元文件格式放置在剪贴板上,如果这是您想要的。 任何从剪贴板粘贴的应用程序都将/应该提取最适合其要求的格式。