Search code examples
c++windowsbitmapdibcreatedibsection

CreateDIBSection leaving 'Not enough storage' error, but seems to still work anyway


Whenever my app tries to create a DIB section, either by calling CreateDIBSection(), or by calling LoadImage() with the LR_CREATEDIBSECTION flag, it seems to return successfully. The HBITMAP it returns is valid, and I can manipulate and display it just fine.

However, calls to GetLastError() will return 8: Not enough storage is available to process this command. This happens from the very first call to the last. The size of the bitmap requested seems inconsequential; 800x600 or 16x16, same result. Immediately prior to the function call, GetLastError() returns no error; additionally, calling SetLastError(0) before the function call has the same result.

I have found other people asking similar questions, but it either turns out they are using CreateCompatibleBitmap() and the problem goes away when they switch to CreateDIBSection(), or they are already using CreateDIBSection() and the result it returns is invalid and so is not working at all.

Since things seem to be working, I have thought I could just ignore it (and call SetLastError(0) after calls to either function), but there could be some subtle problem I am overlooking by doing so.

And of course, here's some of the basic code I'm using. First, the call to LoadImage(), which is part of a basic bitmap class that I use for a lot of things, and which I simplified quite a bit to show the more relevant aspects:

bool Bitmap::Load( const char* szBitmapName, /*...*/ )
{
   m_hBitmap = (HBITMAP)LoadImage( GetModuleHandle( NULL ), szBitmapName,
            IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE );
   //...
}
// ...
Bitmap::~Bitmap()
{
   if( m_hBitmap ) DeleteObject( m_hBitmap );
}
bool Bitmap::Draw( HDC hDC, int iDstX, int iDstY, int iDstWidth,
                   int iDstHeight, int iSrcX, int iSrcY, int iSrcWidth,
                   int iSrcHeight, bool bUseMask ) const
{
   HDC hdcMem = CreateCompatibleDC( hDC );
   if( hdcMem == NULL ) return false;
   HBITMAP hOld = (HBITMAP)SelectObject( hdcMem, m_hBitmap );
   BLENDFUNCTION blendFunc;
   blendFunc.BlendOp = AC_SRC_OVER;
   blendFunc.BlendFlags = 0;
   blendFunc.AlphaFormat = AC_SRC_ALPHA;
   blendFunc.SourceConstantAlpha = 255;
   AlphaBlend( hDC, iDstX, iDstY, iDstWidth, iDstHeight, hdcMem, iSrcX,
               iSrcY, iSrcWidth, iSrcHeight, blendFunc );
   SelectObject( hdcMem, hOld );
   DeleteDC( hdcMem );
}

Calls to CreateDIBSection are typically done when updating a layered window:

HDC hDCScreen( GetDC(0) );
POINT tSourcePos = { 0, 0 };
HDC hDCSource( CreateCompatibleDC( hDCScreen ) );
// iWidth and iHeight are used both for the bitmap size and window size
// to keep this example simpler
BITMAPINFO bi32 = {0};
bi32.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi32.bmiHeader.biWidth = iWidth;
bi32.bmiHeader.biHeight = iHeight;
bi32.bmiHeader.biPlanes = 1;
bi32.bmiHeader.biBitCount = 32;
bi32.bmiHeader.biCompression = BI_RGB;
void* pBits = NULL;
HBITMAP hBitmap = CreateDIBSection(NULL, &bi32, DIB_RGB_COLORS,
                  (void**)&pBits, NULL, NULL);

HBITMAP hOldBitmap = (HBITMAP)SelectObject( hDCSource, hBitmap );
POINT tWindowPos = { 0, 0 };
SIZE tWindowSize = { iWidth, iHeight };
BLENDFUNCTION blendFunction = {0};
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.SourceConstantAlpha = 255;
blendFunction.AlphaFormat = AC_SRC_ALPHA;
DWORD iFlags( ULW_ALPHA );

// m_tBitmap is an instance of Bitmap class previously mentioned
m_tBitmap.Draw( hDCSource, 0, 0, iWidth, iHeight, 0, 0, iWidth, iHeight );
UpdateLayeredWindow( GetHandle(), hDCScreen, &tWindowPos, &tWindowSize,
                     hDCSource, &tSourcePos, 0, &blendFunction, iFlags );
SelectObject( hDCSource, hOldBitmap );
DeleteObject( hBitmap );
DeleteDC( hDCSource );
ReleaseDC( 0, hDCScreen );

Any pointers about anything I'm completely off-base about would be appreciated.


Solution

  • I think you aren't following what the documentation says (from CreateDIBSection):

    If the function succeeds, the return value is a handle to the newly created DIB, and *ppvBits points to the bitmap bit values.

    If the function fails, the return value is NULL, and *ppvBits is NULL.

    This function can return the following value. [...]

    If the return value is not NULL, the function succeeded. Calling GetLastError won't necessarily return any reliably meaningful information on success (from GetLastError):

    If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.