65.9K
CodeProject 正在变化。 阅读更多。
Home

复制 DIB 到剪贴板

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.77/5 (33投票s)

2001 年 10 月 17 日

CPOL

3分钟阅读

viewsIcon

173482

downloadIcon

1627

帮助将设备无关位图复制到剪贴板。

引言

最近,我需要将 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 格式、设备相关格式和图元文件格式放置在剪贴板上,如果这是您想要的。 任何从剪贴板粘贴的应用程序都将/应该提取最适合其要求的格式。

© . All rights reserved.