CreateDIBSection 留下“存储空间不足”错误,但似乎仍然有效

发布于 2024-08-24 19:16:39 字数 3091 浏览 6 评论 0原文

每当我的应用程序尝试通过调用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一抹微笑 2024-08-31 19:16:40

所以呢? 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.

时光磨忆 2024-08-31 19:16:40

我记得在某些情况下,为了从 GetLastError 获取可靠的值,您必须在调用任何 api 函数之前调用 SetLastError(0),然后才能获得正确的值GetLastError() 发生错误。因为如果它成功了,它就不会被更新。

I remember that in some cases, in order to get a reliable value from GetLastError you have to call SetLastError(0) before calling any api function and then later you get the correct error with GetLastError(). Because in case that it succeeded it was not being updated.

傲性难收 2024-08-31 19:16:39

我认为您没有遵循文档的说明(来自 创建DIBSection):

如果函数成功,则返回
value 是新创建的句柄
DIB 和 *ppvBits 指向位图
位值。

如果函数失败,则返回
值为 NULL*ppvBitsNULL

该函数可以返回以下值。 [...]

如果返回值不为NULL,则函数成功。调用 GetLastError 不一定会返回任何有关成功的可靠且有意义的信息(来自 GetLastError):

如果该函数没有记录到
设置最后一个错误代码,值
该函数返回的内容很简单
最近的最后一个错误代码
已设定;一些函数设置
成功时最后一个错误代码为 0,并且
其他人则不然。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文