【问题标题】:Save HBITMAP to *.bmp file using only Win32仅使用 Win32 将 HBITMAP 保存到 *.bmp 文件
【发布时间】:2014-09-03 10:05:08
【问题描述】:

我的纯 Win32 项目中有一个 HBITMAP(不使用外部库)。我可以只使用 Winapi 和/或 CRT 函数将其导出到 *.bmp 文件,这样我就不必向项目添加依赖项了吗?

【问题讨论】:

  • stackoverflow.com/questions/24644709/… 到底应该是这个问题的副本,因为它是在 4 天前提出并回答的事实?这在物理上有多可能?
  • @enhzflep 他们是彼此的副本。谁先得到更多合格的答案,另一个将被关闭。这与提问的日期和时间无关。
  • @user814412 - 我不知道你的第一语言是什么,以及对应词的含义是什么。在英语中,一个是另一个的副本。重复的 ALWAYS 排在第二位。一个类似的词是复制。某物的第一个实例是原件,下一个是副本或副本。这与时间和日期有关一切
  • @enhzflep 我认为duplicatetriplicate等词来自replicate,这个词,意思是复制一些东西。 你是对的那个 du 前缀 ALWAYS 意味着 2, 因此是一个 duplicate 需要定义一个原始实例。我认为这里的问题在于 SO 对其规则的措辞。我相信他们的意思是 replicatereplica 而不是 duplicate ,因为我们已经在两个实例中看到了很多后者的例子,因为这个例子被关闭了。这又是一个例子。请不要为难自己。
  • 我的意思是former,但写错了latter

标签: c windows winapi bitmapimage hbitmap


【解决方案1】:

没有 API 可以直接保存到文件中,因为通常拥有位图句柄并不意味着您可以直接访问位图数据。您的解决方案是将位图复制到另一个具有数据访问 (DIB) 的位图中,然后使用它的数据写入文件。

您通常要么使用CreateDIBSection 创建另一个位图,要么使用GetDIBits 获取位图数据。

CreateFile, WriteFile 将数据写入文件。

您编写:BITMAPFILEHEADER,然后是 BITMAPINFOHEADER,然后是调色板(当比特/像素超过 8 时通常没有调色板),然后是数据本身。

另见:

代码

这是来自 MSDN 文章的代码(注意您需要定义errhandler() 函数):

PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{ 
    BITMAP bmp; 
    PBITMAPINFO pbmi; 
    WORD    cClrBits; 

    // Retrieve the bitmap color format, width, and height.  
    if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) 
        errhandler("GetObject", hwnd); 

    // Convert the color format to a count of bits.  
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 
    if (cClrBits == 1) 
        cClrBits = 1; 
    else if (cClrBits <= 4) 
        cClrBits = 4; 
    else if (cClrBits <= 8) 
        cClrBits = 8; 
    else if (cClrBits <= 16) 
        cClrBits = 16; 
    else if (cClrBits <= 24) 
        cClrBits = 24; 
    else cClrBits = 32; 

    // Allocate memory for the BITMAPINFO structure. (This structure  
    // contains a BITMAPINFOHEADER structure and an array of RGBQUAD  
    // data structures.)  

    if (cClrBits < 24) 
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
        sizeof(BITMAPINFOHEADER) + 
        sizeof(RGBQUAD) * (1<< cClrBits)); 

    // There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel 

    else 
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
        sizeof(BITMAPINFOHEADER)); 

    // Initialize the fields in the BITMAPINFO structure.  

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    pbmi->bmiHeader.biWidth = bmp.bmWidth; 
    pbmi->bmiHeader.biHeight = bmp.bmHeight; 
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 
    if (cClrBits < 24) 
        pbmi->bmiHeader.biClrUsed = (1<<cClrBits); 

    // If the bitmap is not compressed, set the BI_RGB flag.  
    pbmi->bmiHeader.biCompression = BI_RGB; 

    // Compute the number of bytes in the array of color  
    // indices and store the result in biSizeImage.  
    // The width must be DWORD aligned unless the bitmap is RLE 
    // compressed. 
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
        * pbmi->bmiHeader.biHeight; 
    // Set biClrImportant to 0, indicating that all of the  
    // device colors are important.  
    pbmi->bmiHeader.biClrImportant = 0; 
    return pbmi; 
} 

void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, 
                   HBITMAP hBMP, HDC hDC) 
{ 
    HANDLE hf;                 // file handle  
    BITMAPFILEHEADER hdr;       // bitmap file-header  
    PBITMAPINFOHEADER pbih;     // bitmap info-header  
    LPBYTE lpBits;              // memory pointer  
    DWORD dwTotal;              // total count of bytes  
    DWORD cb;                   // incremental count of bytes  
    BYTE *hp;                   // byte pointer  
    DWORD dwTmp; 

    pbih = (PBITMAPINFOHEADER) pbi; 
    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

    if (!lpBits) 
        errhandler("GlobalAlloc", hwnd); 

    // Retrieve the color table (RGBQUAD array) and the bits  
    // (array of palette indices) from the DIB.  
    if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, 
        DIB_RGB_COLORS)) 
    {
        errhandler("GetDIBits", hwnd); 
    }

    // Create the .BMP file.  
    hf = CreateFile(pszFile, 
        GENERIC_READ | GENERIC_WRITE, 
        (DWORD) 0, 
        NULL, 
        CREATE_ALWAYS, 
        FILE_ATTRIBUTE_NORMAL, 
        (HANDLE) NULL); 
    if (hf == INVALID_HANDLE_VALUE) 
        errhandler("CreateFile", hwnd); 
    hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"  
    // Compute the size of the entire file.  
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + 
        pbih->biSize + pbih->biClrUsed 
        * sizeof(RGBQUAD) + pbih->biSizeImage); 
    hdr.bfReserved1 = 0; 
    hdr.bfReserved2 = 0; 

    // Compute the offset to the array of color indices.  
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + 
        pbih->biSize + pbih->biClrUsed 
        * sizeof (RGBQUAD); 

    // Copy the BITMAPFILEHEADER into the .BMP file.  
    if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), 
        (LPDWORD) &dwTmp,  NULL)) 
    {
        errhandler("WriteFile", hwnd); 
    }

    // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.  
    if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) 
        + pbih->biClrUsed * sizeof (RGBQUAD), 
        (LPDWORD) &dwTmp, ( NULL)))
        errhandler("WriteFile", hwnd); 

    // Copy the array of color indices into the .BMP file.  
    dwTotal = cb = pbih->biSizeImage; 
    hp = lpBits; 
    if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) 
        errhandler("WriteFile", hwnd); 

    // Close the .BMP file.  
    if (!CloseHandle(hf)) 
        errhandler("CloseHandle", hwnd); 

    // Free memory.  
    GlobalFree((HGLOBAL)lpBits);
}

【讨论】:

  • 实际上,您可以保存HBITMAP,而无需手动转换。在文件上创建流,创建编码器,然后将帧转储到其中需要一些代码。但是,繁重的工作是在 IWICImagingFactory::CreateBitmapFromHBITMAPIWICBitmapEncoder 中完成的,因此您不必摆弄位图标题。
【解决方案2】:

我将把这个独立的概念证明留在这里,因为我可能需要稍后查看它,因为它并不明显。它会截取桌面窗口的屏幕截图并将其保存到 bitmap.bmp 中:

#include <Windows.h>
#include <stdio.h>
#include <assert.h>

/* forward declarations */
PBITMAPINFO CreateBitmapInfoStruct(HBITMAP);
void CreateBMPFile(LPTSTR pszFile, HBITMAP hBMP); 
int main(int argc, char **argv);

PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp)
{ 
    BITMAP bmp; 
    PBITMAPINFO pbmi; 
    WORD    cClrBits; 

    // Retrieve the bitmap color format, width, and height.  
    assert(GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)); 

    // Convert the color format to a count of bits.  
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 
    if (cClrBits == 1) 
        cClrBits = 1; 
    else if (cClrBits <= 4) 
        cClrBits = 4; 
    else if (cClrBits <= 8) 
        cClrBits = 8; 
    else if (cClrBits <= 16) 
        cClrBits = 16; 
    else if (cClrBits <= 24) 
        cClrBits = 24; 
    else cClrBits = 32; 

    // Allocate memory for the BITMAPINFO structure. (This structure  
    // contains a BITMAPINFOHEADER structure and an array of RGBQUAD  
    // data structures.)  

     if (cClrBits < 24) 
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
                    sizeof(BITMAPINFOHEADER) + 
                    sizeof(RGBQUAD) * (1<< cClrBits)); 

     // There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel 

     else 
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
                    sizeof(BITMAPINFOHEADER)); 

    // Initialize the fields in the BITMAPINFO structure.  

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    pbmi->bmiHeader.biWidth = bmp.bmWidth; 
    pbmi->bmiHeader.biHeight = bmp.bmHeight; 
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 
    if (cClrBits < 24) 
        pbmi->bmiHeader.biClrUsed = (1<<cClrBits); 

    // If the bitmap is not compressed, set the BI_RGB flag.  
    pbmi->bmiHeader.biCompression = BI_RGB; 

    // Compute the number of bytes in the array of color  
    // indices and store the result in biSizeImage.  
    // The width must be DWORD aligned unless the bitmap is RLE 
    // compressed. 
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
                                  * pbmi->bmiHeader.biHeight; 
    // Set biClrImportant to 0, indicating that all of the  
    // device colors are important.  
     pbmi->bmiHeader.biClrImportant = 0; 
     return pbmi; 
 } 

void CreateBMPFile(LPTSTR pszFile, HBITMAP hBMP) 
 { 
     HANDLE hf;                 // file handle  
    BITMAPFILEHEADER hdr;       // bitmap file-header  
    PBITMAPINFOHEADER pbih;     // bitmap info-header  
    LPBYTE lpBits;              // memory pointer  
    DWORD dwTotal;              // total count of bytes  
    DWORD cb;                   // incremental count of bytes  
    BYTE *hp;                   // byte pointer  
    DWORD dwTmp;     
    PBITMAPINFO pbi;
    HDC hDC;

    hDC = CreateCompatibleDC(GetWindowDC(GetDesktopWindow()));
    SelectObject(hDC, hBMP);

    pbi = CreateBitmapInfoStruct(hBMP);

    pbih = (PBITMAPINFOHEADER) pbi; 
    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

    assert(lpBits) ;

    // Retrieve the color table (RGBQUAD array) and the bits  
    // (array of palette indices) from the DIB.  
    assert(GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, 
        DIB_RGB_COLORS));

    // Create the .BMP file.  
    hf = CreateFile(pszFile, 
                   GENERIC_READ | GENERIC_WRITE, 
                   (DWORD) 0, 
                    NULL, 
                   CREATE_ALWAYS, 
                   FILE_ATTRIBUTE_NORMAL, 
                   (HANDLE) NULL); 
    assert(hf != INVALID_HANDLE_VALUE) ;

    hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"  
    // Compute the size of the entire file.  
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + 
                 pbih->biSize + pbih->biClrUsed 
                 * sizeof(RGBQUAD) + pbih->biSizeImage); 
    hdr.bfReserved1 = 0; 
    hdr.bfReserved2 = 0; 

    // Compute the offset to the array of color indices.  
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + 
                    pbih->biSize + pbih->biClrUsed 
                    * sizeof (RGBQUAD); 

    // Copy the BITMAPFILEHEADER into the .BMP file.  
    assert(WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), 
        (LPDWORD) &dwTmp,  NULL)); 

    // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.  
    assert(WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) 
                  + pbih->biClrUsed * sizeof (RGBQUAD), 
                  (LPDWORD) &dwTmp, ( NULL)));

    // Copy the array of color indices into the .BMP file.  
    dwTotal = cb = pbih->biSizeImage; 
    hp = lpBits; 
    assert(WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)); 

    // Close the .BMP file.  
     assert(CloseHandle(hf)); 

    // Free memory.  
    GlobalFree((HGLOBAL)lpBits);
}

int main(int argc, char **argv)
{
    HWND hwnd;
    HDC hdc[2];
    HBITMAP hbitmap;
    RECT rect;

    hwnd = GetDesktopWindow();
    GetClientRect(hwnd, &rect);
    hdc[0] = GetWindowDC(hwnd);
    hbitmap = CreateCompatibleBitmap(hdc[0], rect.right, rect.bottom); 
    hdc[1] = CreateCompatibleDC(hdc[0]);
    SelectObject(hdc[1], hbitmap);    

    BitBlt (    
        hdc[1],
        0,
        0,
        rect.right,
        rect.bottom,
        hdc[0],
        0,
        0,
        SRCCOPY
    );

    CreateBMPFile("bitmap.bmp", hbitmap);
    return 0;
}

【讨论】:

  • 感谢 Dimitry 提供了这么好的代码块。你让我今天一整天都感觉很好。干杯!
  • 要将 bmp 文件保存到 D 盘使用 CreateBMPFile("D:\\bitmap.bmp", hbitmap);
  • @Dmitry @Martin 有一个错误提示:uninitialized local variable 'bmp' used。代码已经声明了BITMAP bmp。为什么?
  • @JAMESBRYANB.Juventud 我的猜测是因为您将其编译为 c++ 编译器或其他编译器。如果你不说警告是哪一行,我无法判断,但它确实是使用 GetObject 初始化并检查 GetObject 是否成功。
  • @JAMESBRYANB.Juventud 可能有一些关于使用变量而不首先使用赋值运算符给它赋值的新警告。由于 GetObject 分配值而不是您直接分配,因此它可能希望您在使用它之前将其设置为 0 或 INVALID_HANDLE。该代码仅在 .c 文件的设置中在 C 编译模式下针对 Microsoft MSVC 进行了测试,因此它可能仅是 C++ 警告,因为编译器不确定 GetObject 是否分配该值,请尝试将其分配给 0 或在任何地方使用 INVALID_HANDLE 或 NULL 之前,看看它是否仍然抱怨。
【解决方案3】:

是的,这是可能的,使用 Windows Imaging Component (WIC)。 WIC 提供内置编码器,因此您不必手动写出位图标题和数据。它还允许您选择不同的编码器(例如 PNG),只需更改一行代码。

这个过程相当简单。它由以下步骤组成:

  1. 使用GetObject(尺寸、位深度)从源HBITMAP 检索属性。
  2. 创建一个IWICImagingFactory 实例。
  3. HBITMAP (IWICImagingFactory::CreateBitmapFromHBITMAP) 创建一个IWICBitmap 实例。
  4. 创建一个IWICStream 实例 (IWICImagingFactory::CreateStream),并将其附加到文件名 (IWICStream::InitializeFromFilename)。
  5. 创建一个 IWICBitmapEncoder 实例 (IWICImagingFactory::CreateEncoder),并将其与流 (IWICBitmapEncoder::Initialize) 关联。
  6. 创建一个IWICBitmapFrameEncode实例(IWICBitmapEncoder::CreateNewFrame),并按照源HBITMAPIWICBitmapFrameEncode::InitializeIWICBitmapFrameEncode::SetSizeIWICBitmapFrameEncode::SetPixelFormat)对其进行初始化。
  7. 将位图数据写入帧 (IWICBitmapFrameEncode::WriteSource)。
  8. 将帧和数据提交到流式传输(IWICBitmapFrameEncode::CommitIWICBitmapEncoder::Commit)。

翻译成代码:

#define COBJMACROS

#include <Objbase.h>
#include <wincodec.h>
#include <Windows.h>
#include <Winerror.h>

#pragma comment(lib, "Windowscodecs.lib")

HRESULT WriteBitmap(HBITMAP bitmap, const wchar_t* pathname) {

    HRESULT hr = S_OK;

    // (1) Retrieve properties from the source HBITMAP.
    BITMAP bm_info = { 0 };
    if (!GetObject(bitmap, sizeof(bm_info), &bm_info))
        hr = E_FAIL;

    // (2) Create an IWICImagingFactory instance.
    IWICImagingFactory* factory = NULL;
    if (SUCCEEDED(hr))
        hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
                              &IID_IWICImagingFactory, &factory);

    // (3) Create an IWICBitmap instance from the HBITMAP.
    IWICBitmap* wic_bitmap = NULL;
    if (SUCCEEDED(hr))
        hr = IWICImagingFactory_CreateBitmapFromHBITMAP(factory, bitmap, NULL,
                                                        WICBitmapIgnoreAlpha,
                                                        &wic_bitmap);

    // (4) Create an IWICStream instance, and attach it to a filename.
    IWICStream* stream = NULL;
    if (SUCCEEDED(hr))
        hr = IWICImagingFactory_CreateStream(factory, &stream);
    if (SUCCEEDED(hr))
        hr = IWICStream_InitializeFromFilename(stream, pathname, GENERIC_WRITE);

    // (5) Create an IWICBitmapEncoder instance, and associate it with the stream.
    IWICBitmapEncoder* encoder = NULL;
    if (SUCCEEDED(hr))
        hr = IWICImagingFactory_CreateEncoder(factory, &GUID_ContainerFormatBmp, NULL,
                                              &encoder);
    if (SUCCEEDED(hr))
        hr = IWICBitmapEncoder_Initialize(encoder, (IStream*)stream,
                                          WICBitmapEncoderNoCache);

    // (6) Create an IWICBitmapFrameEncode instance, and initialize it
    // in compliance with the source HBITMAP.
    IWICBitmapFrameEncode* frame = NULL;
    if (SUCCEEDED(hr))
        hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL);
    if (SUCCEEDED(hr))
        hr = IWICBitmapFrameEncode_Initialize(frame, NULL);
    if (SUCCEEDED(hr))
        hr = IWICBitmapFrameEncode_SetSize(frame, bm_info.bmWidth, bm_info.bmHeight);
    if (SUCCEEDED(hr)) {
        GUID pixel_format = GUID_WICPixelFormat24bppBGR;
        hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format);
    }

    // (7) Write bitmap data to the frame.
    if (SUCCEEDED(hr))
        hr = IWICBitmapFrameEncode_WriteSource(frame, (IWICBitmapSource*)wic_bitmap,
                                               NULL);

    // (8) Commit frame and data to stream.
    if (SUCCEEDED(hr))
        hr = IWICBitmapFrameEncode_Commit(frame);
    if (SUCCEEDED(hr))
        hr = IWICBitmapEncoder_Commit(encoder);

    // Cleanup
    if (frame)
        IWICBitmapFrameEncode_Release(frame);
    if (encoder)
        IWICBitmapEncoder_Release(encoder);
    if (stream)
        IWICStream_Release(stream);
    if (wic_bitmap)
        IWICBitmap_Release(wic_bitmap);
    if (factory)
        IWICImagingFactory_Release(factory);

    return hr;
}

这里有一个配套的测试应用程序来展示用法。确保在包含任何系统标头之前#define OEMRESOURCE 以允许使用OBM_ 图像。

int wmain(int argc, wchar_t** argv) {

    HRESULT hr = S_OK;
    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr))
        return -1;

    HBITMAP bitmap = LoadImage(NULL, MAKEINTRESOURCE(OBM_ZOOM), IMAGE_BITMAP, 0, 0,
                               LR_DEFAULTCOLOR);

    hr = WriteBitmap(bitmap, argv[1]);

    // Cleanup
    if (bitmap)
        DeleteObject(bitmap);

    CoUninitialize();
    return 0;
}

这将加载系统提供的位图,并将其保存到指定为命令行参数的路径名。

限制:

  • 不支持 Alpha 通道。虽然位图版本 5 支持 Alpha 通道,但我不知道有什么方法可以找出 HBITMAP 是否指的是具有 Alpha 通道的位图,我也不知道如何确定它是否是预乘的。如果您确实想支持 Alpha 通道,请确保将 EnableV5Header32bppBGRA 属性设置为 VARIANT_TRUE(请参阅 BMP Format: Encoding)。
  • 不支持托盘化位图 (bpp IWICImagingFactory::CreateBitmapFromHBITMAP 的调用中提供适当的 HPALETTE
  • 编码器使用GUID_WICPixelFormat24bppBGR 像素格式常数进行初始化。更通用的实现会从源 HBITMAP 推导出兼容的像素格式。

【讨论】:

  • 要支持 alpha 通道(当 Windows 支持时),在 CreateBitmapFromHBITMAP 中使用 WICBitmapUseAlpha,在 CreateNewFrame 中获取一个 IPropertyBag2,将 'EnableV5Header32bppBGRA' 写入 VARIANT_TRUE 给它,并将这个包传递给框架的 Initialize(不要检查错误以继续在旧 Windows 上工作)。 docs.microsoft.com/en-us/windows/win32/wic/…
【解决方案4】:

另一个简约的选择是使用 OLE 的IPicture。它一直存在,仍然是 Win32 API 的一部分:

#define _S(exp) (([](HRESULT hr) { if (FAILED(hr)) _com_raise_error(hr); return hr; })(exp));

PICTDESC pictdesc = {};
pictdesc.cbSizeofstruct = sizeof(pictdesc);
pictdesc.picType = PICTYPE_BITMAP;
pictdesc.bmp.hbitmap = hBitmap;

CComPtr<IPicture> picture;
_S( OleCreatePictureIndirect(&pictdesc, __uuidof(IPicture), FALSE, (LPVOID*)&picture) );

// Save to a stream

CComPtr<IStream> stream;
_S( CreateStreamOnHGlobal(NULL, TRUE, &stream) );
LONG cbSize = 0;
_S( picture->SaveAsFile(stream, TRUE, &cbSize) );

// Or save to a file

CComPtr<IPictureDisp> disp;
_S( picture->QueryInterface(&disp) );
_S( OleSavePictureFile(disp, CComBSTR("C:\\Temp\\File.bmp")) );

【讨论】:

  • hBitmap 中的 pictdesc.bmp.hbitmap = hBitmap; 似乎未声明。你知道如何解决吗?
  • @JAMESBRYANB.Juventud,hBitmap 是你已经拥有的,根据 OP 的问题,HBITMAP 类型。这是您要将其内容保存到文件的位图的 Win32 句柄。
  • 这是我的错。我还没有完全阅读OP的问题。他想要的只是出口。所以基本上,他已经有一个bitmap。谢谢!
【解决方案5】:

HBITMAP*.bmp 文件的一个功能代码。

BOOL SaveHBITMAPToFile(HBITMAP hBitmap, LPCTSTR lpszFileName)
{
    HDC hDC;
    int iBits;
    WORD wBitCount;
    DWORD dwPaletteSize = 0, dwBmBitsSize = 0, dwDIBSize = 0, dwWritten = 0;
    BITMAP Bitmap0;
    BITMAPFILEHEADER bmfHdr;
    BITMAPINFOHEADER bi;
    LPBITMAPINFOHEADER lpbi;
    HANDLE fh, hDib, hPal, hOldPal2 = NULL;
    hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
    iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
    DeleteDC(hDC);
    if (iBits <= 1)
        wBitCount = 1;
    else if (iBits <= 4)
        wBitCount = 4;
    else if (iBits <= 8)
        wBitCount = 8;
    else
        wBitCount = 24;
    GetObject(hBitmap, sizeof(Bitmap0), (LPSTR)&Bitmap0);
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = Bitmap0.bmWidth;
    bi.biHeight = -Bitmap0.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = wBitCount;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrImportant = 0;
    bi.biClrUsed = 256;
    dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount + 31) & ~31) / 8
        * Bitmap0.bmHeight;
    hDib = GlobalAlloc(GHND, dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
    *lpbi = bi;

    hPal = GetStockObject(DEFAULT_PALETTE);
    if (hPal)
    {
        hDC = GetDC(NULL);
        hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
        RealizePalette(hDC);
    }


    GetDIBits(hDC, hBitmap, 0, (UINT)Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
        + dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);

    if (hOldPal2)
    {
        SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE);
        RealizePalette(hDC);
        ReleaseDC(NULL, hDC);
    }

    fh = CreateFile(lpszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

    if (fh == INVALID_HANDLE_VALUE)
        return FALSE;

    bmfHdr.bfType = 0x4D42; // "BM"
    dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
    bmfHdr.bfSize = dwDIBSize;
    bmfHdr.bfReserved1 = 0;
    bmfHdr.bfReserved2 = 0;
    bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;

    WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);

    WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
    GlobalUnlock(hDib);
    GlobalFree(hDib);
    CloseHandle(fh);
    return TRUE;
}

【讨论】:

  • 我觉得这很有帮助,但这里有一个问题:一旦将HBITMAP 写入文件,我就不能在资源文件中重用它(.rc / .o),因为它的图像标题显然不兼容。我即兴创作了一种方法,在 Paint 中打开文件,然后将其保存,将图像标题部分替换为 Paint 的默认部分,这样它就可以重复使用。为了模拟这种效果(因为我需要它在批处理中可行),我尝试复制 Paint 的默认图像标题,但到目前为止我一直无法这样做。你能看看这件事吗?
  • 如果你能把Paint的默认图片头添加到这段代码中,那就太好了。我的意思是,我们如何使用默认情况下 Paint 适用于.bmp 文件的相同文件/图像标题设置?我是一个业余爱好者,这就是为什么我不能自己做。提前致谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 2014-08-29
  • 1970-01-01
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多