CreateDIBSection 留下“存储空间不足”错误,但似乎仍然有效
每当我的应用程序尝试通过调用 CreateDIBSection() 或使用 LR_CREATEDIBSECTION
标志调用 LoadImage() 来创建 DIB 部分时,它似乎都会成功返回。它返回的 HBITMAP 是有效的,我可以很好地操作和显示它。
但是,调用 GetLastError() 将返回8:没有足够的存储空间来处理此命令。
从第一次调用到最后一次调用都会发生这种情况。所请求的位图的大小似乎无关紧要; 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 );
任何关于我完全偏离基础的事情的指示将不胜感激。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
所以呢? CreateDIBSection 是一个复杂的函数,它利用许多其他 Windows API 来完成其工作。其中一些 API 可以设置最后一个错误以反映其内部状态。 CreateDIBSection 不会清除错误,只是为了在没有失败时返回 0。
合约规定,当失败时,GetLastError 被设置为一个有意义的值:它并没有说明当 CreateDIBSection 返回时,最后一个错误值对调用者有任何意义。
如果内存不足,很可能会抛出异常和/或为了简化代码,最后一个错误值只是在分配顶部附近抢先设置,以便在分配时无需进一步努力即可正确设置失败或抛出异常。
So what? CreateDIBSection is a complicated function which makes use of many other windows APIs do its job. Some of those APIs could set the last error to reflect their internal state. CreateDIBSection isn't going to clear the error, just so that a 0 can be returned when it doesn't fail.
The contract says, when it fails, GetLastError is set to a meaningful value: it does NOT say that the last error value has any meaning to the caller when CreateDIBSection does return.
Its quite possible that, if there is a lack of memory, an exception gets thrown and/or to simplify the code the last error value simply gets preemptively set near the top of the allocations such that its correctly set with no further effort should the allocations fail or throw an exception.
我记得在某些情况下,为了从
GetLastError
获取可靠的值,您必须在调用任何 api 函数之前调用SetLastError(0)
,然后才能获得正确的值GetLastError() 发生错误。因为如果它成功了,它就不会被更新。
I remember that in some cases, in order to get a reliable value from
GetLastError
you have to callSetLastError(0)
before calling any api function and then later you get the correct errorwith GetLastError()
. Because in case that it succeeded it was not being updated.我认为您没有遵循文档的说明(来自 创建DIBSection):
如果返回值不为
NULL
,则函数成功。调用GetLastError
不一定会返回任何有关成功的可靠且有意义的信息(来自 GetLastError):I think you aren't following what the documentation says (from CreateDIBSection):
If the return value is not
NULL
, the function succeeded. CallingGetLastError
won't necessarily return any reliably meaningful information on success (from GetLastError):