c++ - GetDIBits() 将目标指针设置为 NULL 且没有错误
问题描述
我正在尝试将应用程序图标放入 char 数组中。下面的代码将 HICON 转换为 BITMAP,然后尝试将 BITMAP 中的字节提取到 char 数组中。当我单步执行代码时,我观察到第二个 GetDIBits() 将目标指针修改为 NULL,尽管声称写入了 16 个字节。这种行为非常令人费解。我怀疑强制BITMAPINFOHEADER*
转换BITMAPINFO*
可能有问题,但是BITMAPINFO
在退出函数时直接使用 a 会导致堆栈损坏。有谁知道为什么 GetDIBits() 会这样?
std::unique_ptr<char> getRawImg(HICON& icon)
{
// step 1 : get a bitmap from an application icon
ICONINFO iconInfo;
ZeroMemory(&iconInfo, sizeof(iconInfo));
BITMAP bitMap;
ZeroMemory(&bitMap, sizeof(bitMap));
HRESULT bRes = GetIconInfo(icon, &iconInfo);
int width;
int height;
int bitsPerPixel;
if (iconInfo.hbmColor) // color icon
{
if (GetObject(iconInfo.hbmColor, sizeof(bitMap), &bitMap))
{
width = bitMap.bmWidth;
height = bitMap.bmHeight;
bitsPerPixel = bitMap.bmBitsPixel;
}
}
else if (iconInfo.hbmMask) // black and white icon
{
if (GetObject(iconInfo.hbmMask, sizeof(bitMap), &bitMap))
{
width = bitMap.bmWidth;
height = bitMap.bmHeight / 2;
bitsPerPixel = 1;
}
}
// step 2 : extract bytes from the bitmap into a byte array
HBITMAP hBitmap = CreateBitmapIndirect(&bitMap);
int stride = (width * bitsPerPixel + 31) / 32 * 4;
HDC hdc = GetDC(NULL);
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = height;
bi.biPlanes = 1;
bi.biBitCount = bitsPerPixel;
bi.biCompression = BI_RGB;
bi.biSizeImage = stride * height;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
BITMAPINFO* pBitmapInfo = (BITMAPINFO*)&bi;
if (!GetDIBits(hdc, hBitmap, 0, 0, NULL, pBitmapInfo, DIB_RGB_COLORS)) {
// error
std::cout << "failed to get bitmap info" << std::endl;
}
std::unique_ptr<char> buffer(new char[bi.biWidth * bi.biHeight * bi.biBitCount / 8]);
// Buffer points to some address before calling GetDIBits(). After calling GetDIBits(), buffer points to NULL. bytesWritten is 16
int bytesWritten = GetDIBits(hdc, hBitmap, 0, height, (LPVOID)buffer.get(), pBitmapInfo, DIB_RGB_COLORS);
if (bytesWritten <= 0) {
// error
std::cout << "failed" << std::endl;
}
DeleteObject(hBitmap);
ReleaseDC(NULL, hdc);
if (iconInfo.hbmColor)
DeleteObject(iconInfo.hbmColor);
if (iconInfo.hbmMask)
DeleteObject(iconInfo.hbmMask);
return buffer;
}
解决方案
您需要分配一个适当大小的BITMAPINFO
并将其转换为BITMAPINFOHEADER*
(或仅使用其bmiHeader
成员)。不分配 aBITMAPINFOHEADER
并将其转换为BITMAPINFO*
. ABITMAPINFO
由 aBITMAPINFOHEADER
后跟一个包含 0 个或多个RGBQUAD
颜色表元素的数组组成。ABITMAPINFOHEADER
本身不包含颜色表,但它确实描述了它后面的颜色表。
如果请求的 DIB 格式与其内部格式匹配,则复制位图的 RGB 值。如果请求的格式与内部格式不匹配,则会合成一个颜色表...
如果 lpvBits 参数是有效指针,则必须初始化 BITMAPINFOHEADER 结构的前六个成员以指定 DIB 的大小和格式。扫描线必须在 DWORD 上对齐,RLE 压缩位图除外。
通过将高度设置为正数来指定自下而上的 DIB,而通过将高度设置为负数来指定自上而下的 DIB。位图颜色表将附加到 BITMAPINFO 结构中。
如果 lpvBits 为 NULL,GetDIBits 检查 lpbi 指向的第一个结构的第一个成员。此成员必须指定 BITMAPCOREHEADER 或 BITMAPINFOHEADER 结构的大小(以字节为单位)。该函数使用指定的大小来确定应如何初始化剩余成员。
如果 lpvBits 为 NULL 并且 BITMAPINFO 的位计数成员初始化为零,GetDIBits 将填充 BITMAPINFOHEADER 结构或 BITMAPCOREHEADER 而不使用颜色表。此技术可用于查询位图属性。
因此,在您的情况下,您设置lpvBits
为 NULL,但该BITMAPINFOHEADER::biBitCount
字段不是 0,因此GetDIBits()
将尝试填写提供的颜色表BITMAPINFO
,但您没有分配任何内存来接收该颜色表。所以GetDIBits()
最终会破坏BITMAPINFOHEADER
.
推荐阅读
- shopify - 如何在shopify中提高网站加载速度
- netsuite - 如何在套件表单(API 2.0)的选择字段中仅获取父项?
- python - 如果可选属性无效,JSON Schema 会抛出验证错误
- thymeleaf - Thymeleaf - 从选择列表中选择多个选项
- visual-studio-code - Visual Studio 代码烦人的自动启动
- android - 奇怪的绑定适配器点击监听行为kotlin android
- javascript - React webpack config:是否可以仅替换数组中一个插件的配置,而无需重置插件数组?
- mysql - mysql 选择加入第二行
- elasticsearch - ElasticSearch - 使用一个查询的结果作为另一个查询的过滤器
- c# - Directory.GetDirectories 顺序与 Directory.GetFiles 顺序