C语言 仅使用 Win32 将 HBITMAP 保存到 *.bmp 文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24720451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:13:19  来源:igfitidea点击:

Save HBITMAP to *.bmp file using only Win32

cwindowswinapibitmapimagehbitmap

提问by sashoalm

I have a HBITMAP in my pure Win32 project (no external libraries are used). Can I export it to a *.bmp file using only Winapi and/or CRT functions so I don't incur any extra dependencies?

我的纯 Win32 项目中有一个 HBITMAP(没有使用外部库)。我可以只使用 Winapi 和/或 CRT 函数将它导出到 *.bmp 文件,这样我就不会产生任何额外的依赖关系吗?

回答by Roman R.

There is no API to save into file directly because, generally, having a bitmap handle does not mean you have direct access to bitmap data. Your solution is to copy bitmap into another bitmap with data access (DIB) and then using it data to write into file.

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

You typically either create another bitmap using CreateDIBSection, or you get bitmap data with GetDIBits.

您通常使用 来创建另一个位图CreateDIBSection,或者使用获取位图数据GetDIBits

CreateFile, WriteFilewrites data into file.

CreateFile,WriteFile将数据写入文件。

You write: BITMAPFILEHEADER, then BITMAPINFOHEADER, then palette (which you typically don't have when bits/pixel is over 8), then data itself.

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

See also:

也可以看看:

The Code

编码

This is the code from the MSDN article (Notethat you need to define the errhandler()function):

这是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);
}

回答by Dmitry

i'll leave this self contained proof of concept here since I'll probably need to look it up later since it's not obvious. It takes a screenshot of the desktop window and saves it into bitmap.bmp:

我将把这个自包含的概念证明留在这里,因为我可能需要稍后查看它,因为它并不明显。它获取桌面窗口的屏幕截图并将其保存到 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;
}

回答by noseratio

Yet another minimalistic option is to use OLE's IPicture. It's always been around, still a part of Win32 API:

另一个简约的选择是使用 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")) );

回答by IInspectable

Yes, this is possible, using the Windows Imaging Component(WIC). WIC offers built-in encoders, so that you don't have to manually write out bitmap headers and data. It also allows you to choose a different encoder (e.g. PNG), by changing as little as one line of code.

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

The process is fairly straight forward. It consists of the following steps:

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

  1. Retrieve properties from the source HBITMAPusing GetObject(dimensions, bit depth).
  2. Create an IWICImagingFactoryinstance.
  3. Create an IWICBitmapinstance from the HBITMAP(IWICImagingFactory::CreateBitmapFromHBITMAP).
  4. Create an IWICStreaminstance (IWICImagingFactory::CreateStream), and attach it to a filename (IWICStream::InitializeFromFilename).
  5. Create an IWICBitmapEncoderinstance (IWICImagingFactory::CreateEncoder), and associate it with the stream (IWICBitmapEncoder::Initialize).
  6. Create an IWICBitmapFrameEncodeinstance (IWICBitmapEncoder::CreateNewFrame), and initialize it in compliance with the source HBITMAP(IWICBitmapFrameEncode::Initialize, IWICBitmapFrameEncode::SetSize, IWICBitmapFrameEncode::SetPixelFormat).
  7. Write bitmap data to the frame (IWICBitmapFrameEncode::WriteSource).
  8. Commit frame and data to stream (IWICBitmapFrameEncode::Commit, IWICBitmapEncoder::Commit).
  1. HBITMAP使用GetObject(维度,位深度)从源中检索属性。
  2. 创建一个IWICImagingFactory实例。
  3. IWICBitmapHBITMAP( IWICImagingFactory::CreateBitmapFromHBITMAP)创建一个实例。
  4. 创建一个IWICStream实例 ( IWICImagingFactory::CreateStream),并将其附加到文件名 ( IWICStream::InitializeFromFilename)。
  5. 创建一个IWICBitmapEncoder实例 ( IWICImagingFactory::CreateEncoder),并将其与流 ( IWICBitmapEncoder::Initialize)相关联。
  6. 创建一个IWICBitmapFrameEncode实例 ( IWICBitmapEncoder::CreateNewFrame),并根据源HBITMAP( IWICBitmapFrameEncode::Initialize, IWICBitmapFrameEncode::SetSize, IWICBitmapFrameEncode::SetPixelFormat)对其进行初始化。
  7. 将位图数据写入帧 ( IWICBitmapFrameEncode::WriteSource)。
  8. 将帧和数据提交到流 ( IWICBitmapFrameEncode::Commit, IWICBitmapEncoder::Commit)。

Translated to code:

翻译成代码:

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

Here's a companion test application to showcase the usage. Make sure to #define OEMRESOURCEprior to including any system headers to allow use of the OBM_images.

这是一个配套的测试应用程序来展示用法。确保#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;
}

This will load a system-provided bitmap, and save it to the pathname specified as an argument on the command line.

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

Limitations:

限制:

  • No support for alpha channels. Although bitmaps version 5 support alpha channels, I am not aware of any way to find out, whether an HBITMAPrefers to a bitmap with an alpha channel, nor would I know, how to determine, whether it is pre-multiplied. If you do want to support an alpha channel, make sure to set the EnableV5Header32bppBGRAproperty to VARIANT_TRUE(see BMP Format: Encoding).
  • No support for palletized bitmaps (bpp <= 8). If you are dealing with palletized bitmaps, make sure to supply an appropriate HPALETTEin the call to IWICImagingFactory::CreateBitmapFromHBITMAP.
  • The encoder is initialized with the GUID_WICPixelFormat24bppBGRpixel format constant. A more versatile implementation would deduce a compatible pixel format from the source HBITMAP.
  • 不支持 Alpha 通道。虽然位图版本 5 支持 alpha 通道,但我不知道有什么方法可以找出 an 是否HBITMAP指的是带有 alpha 通道的位图,我也不知道如何确定它是否是预乘的。如果您确实想要支持 Alpha 通道,请确保将该EnableV5Header32bppBGRA属性设置为VARIANT_TRUE(请参阅BMP 格式:编码)。
  • 不支持托盘化位图 (bpp <= 8)。如果您正在处理托盘化位图,请确保HPALETTE在对IWICImagingFactory::CreateBitmapFromHBITMAP.
  • 编码器使用GUID_WICPixelFormat24bppBGR像素格式常量进行初始化。更通用的实现将从源中推导出兼容的像素格式HBITMAP

回答by Mark Choi

One function code for HBITMAPto *.bmpfile.

对于一个功能代码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;
}