CreateDIBSection оставляет ошибку «Недостаточно памяти», но, похоже, все равно работает

StackOverflow https://stackoverflow.com/questions/2418776

Вопрос

Всякий раз, когда мое приложение пытается создать раздел DIB, вызывая CreateDIBSection() или вызывая LoadImage() с LR_CREATEDIBSECTION флаг, похоже, он успешно возвращается.А HBITMAP он возвращает действительный, и я могу прекрасно манипулировать им и отображать его.

Однако вызовы GetLastError() вернут 8: Not enough storage is available to process this command. Это происходит от самого первого звонка до последнего.Размер запрошенного растрового изображения кажется несущественным;800x600 или 16x16, результат тот же.Непосредственно перед вызовом функции GetLastError() не возвращает ошибок;кроме того, вызов SetLastError(0) до вызова функции дает тот же результат.

Я нашел других людей, которые задавали подобные вопросы, но либо оказалось, что они используют CreateCompatibleBitmap(), и проблема исчезла, когда они переключились на CreateDIBSection(), либо они уже используют CreateDIBSection(), и результат, который он возвращает, недействителен, и поэтому не работает вообще.

Поскольку кажется, что все работает, я подумал, что могу просто проигнорировать это (и вызвать SetLastError(0) после вызова любой функции), но может возникнуть какая-то тонкая проблема, которую я при этом упускаю из виду.

И, конечно же, вот часть основного кода, который я использую.Во-первых, вызов LoadImage(), который является частью базового класса растровых изображений, который я использую для многих вещей и который я немного упростил, чтобы показать более важные аспекты:

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

Вызовы CreateDIBSection обычно выполняются при обновлении многоуровневого окна:

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 );

Буду признателен за любые указания на то, о чем я совершенно не согласен.

Это было полезно?

Решение

Я думаю, вы не следуете тому, что написано в документации (из СоздатьDIBSection):

Если функция завершается успешно, то возвращается value является дескриптором только что созданного DIB, и *ppvBits указывает на растровое изображение битовые значения.

Если функция завершается ошибкой, возвращается value имеет значение NULL, и *ppvBits является NULL.

Эта функция может возвращать следующее значение.[...]

Если возвращаемое значение не NULL, функция выполнена успешно.Вызов GetLastError не обязательно вернет какую-либо достоверную информацию об успехе (от GetLastError):

Если функция не документирована в Установите код последней ошибки, значение возвращаемый этой функцией, просто Код последней ошибки в были установлены;Некоторые функции устанавливают свойство last-error в 0 при успешном выполнении и другие – нет.

Другие советы

Ну и что?CreateDIBSection — сложная функция, которая использует многие другие API-интерфейсы Windows для выполнения своей работы.Некоторые из те API могут установить последнюю ошибку для отражения их внутреннее состояние.CreateDIBSection не будет очищать ошибку, просто чтобы можно было вернуть 0, если она не сработает.

В контракте сказано, что в случае сбоя GetLastError присваивается значимое значение:он НЕ говорит, что последнее значение ошибки имеет какое-либо значение для вызывающей стороны, когда CreateDIBSection возвращает значение.

Вполне возможно, что при нехватке памяти генерируется исключение и/или для упрощения кода последнее значение ошибки просто заранее устанавливается в верхней части выделенной памяти, так что оно устанавливается правильно без каких-либо дополнительных усилий при выделении. потерпеть неудачу или выдать исключение.

Я помню, что в некоторых случаях, чтобы получить достоверное значение от GetLastError тебе нужно позвонить SetLastError(0) перед вызовом какой-либо функции API, а затем позже вы получите правильную ошибку with GetLastError().Потому что в случае успеха он не обновлялся.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top