Creating HBITMAP from memory buffer

Solution 1:

I'd use CreateCompatibleBitmap, and then call SetDIBits to fill it with your data. These are functions I have seen to work, and SetDIBits is quite flexible, although verbose.

In my MFC years, CreateBitmap was avoided due to suspected performance issues.

Solution 2:

Using GdiPlus I got something that works pretty well and doesn't involve pulling any teeth!

Gdiplus::Bitmap* pBitmap = NULL;
IStream* pStream = NULL;

HRESULT hResult = ::CreateStreamOnHGlobal( NULL, TRUE, &pStream );
if(hResult == S_OK && pStream)
{
    hResult = pStream->Write(&bits[0], ULONG(bits.size()), NULL);
    if(hResult == S_OK)
        pBitmap = Gdiplus::Bitmap::FromStream(pStream);
    pStream->Release();
}

Edit: Changed per Jegatheesh

Solution 3:

A slight variation on the answer provided by AJG85, that employs SHCreateMemStream in place of CreateStreamOnHGlobal. SHCreateMemStream was introduced as a public API in Windows Vista, and provides the benefit of creating an IStream over an existing region in memory, thus avoiding an additional memory allocation:

#include <Shlwapi.h>
#include <atlimage.h>
#include <comdef.h>
#include <comip.h>

#include <vector>

#pragma comment(lib, "Shlwapi.lib")
#if defined(_DEBUG)
#    pragma comment(lib, "comsuppwd.lib")
#else
#    pragma comment(lib, "comsuppw.lib")
#endif


HBITMAP from_data(std::vector<unsigned char> const& data)
{
    if (data.empty())
    {
        _com_issue_error(E_INVALIDARG);
    }

    auto const stream { ::SHCreateMemStream(&data[0], static_cast<UINT>(data.size())) };
    if (!stream)
    {
        _com_issue_error(E_OUTOFMEMORY);
    }
    _COM_SMARTPTR_TYPEDEF(IStream, __uuidof(IStream));
    IStreamPtr sp_stream { stream, false };

    CImage img {};
    _com_util::CheckError(img.Load(sp_stream));

    return img.Detach();
}

This implementation either throws a _com_error, or returns an HBITMAP that refers to the image constructed from the in-memory data.

When the function returns, the memory buffer can be safely freed. The returned HBITMAP is owned by the caller, and needs to be released with a call to DeleteObject.