CreateDIBSection оставляет ошибку «Недостаточно памяти», но, похоже, все равно работает - PullRequest
0 голосов
/ 10 марта 2010

Всякий раз, когда мое приложение пытается создать раздел 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 );

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

Ответы [ 3 ]

3 голосов
/ 10 марта 2010

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

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

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

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

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

Если функция не документирована установить код последней ошибки, значение возвращается этой функцией просто самый последний код последней ошибки были установлены; некоторые функции устанавливают код последней ошибки до 0 в случае успеха и другие нет.

1 голос
/ 10 марта 2010

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

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

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

0 голосов
/ 10 марта 2010

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

...