GetDIBits()存在一个小陷阱,我中招了!希望本文可以帮助踩进去的朋友:)
近日写程序时因要处理位图,所以使用了GetDIBits()这个函数。但在使用过程中,总是抛出异常,提示内存读写错误。
代码如下:
BITMAPINFO Bmi;
HDC hBmpDC = ::GetDC(NULL);
Bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL FgRetVal = FALSE;
if (::GetDIBits(hBmpDC,hBmp,0,0,NULL,&Bmi,DIB_RGB_COLORS)) // 第一次调用,获取位图信息
{
BYTE *pTempBits =new BYTE [Bmi.bmiHeader.biSizeImage]; // 按DIB数据的大小分配内存
if(pTempBits)
{
// 第二次调用,获取位图数据
if (::GetDIBits(hBmpDC, hBmp, 0, (WORD)Bmi.bmiHeader.biHeight, pTempBits, &Bmi, DIB_RGB_COLORS))
{
FgRetVal = TRUE;
}
}
}
反复检查都未能找到出错原因。在检查时还发现,如果打开的是24bits位图或单色图,程序就不会出错,但如果是16色、256色的位图就会异常。
在网上查找相关信息时发现有人也曾经出现过同样问题,但最后也不了了之,没有明确的提示说明问题究竟出在哪。
后来将我的代码与网上找到的调试正常的代码再仔细对比,这时终于发现有个可疑之处:
在我的代码中,第二次调用GetDIBits()前所申请的内存,只考虑了DIB数据的大小,未考虑BITMAPINFO及COLOR TABLE的空间(因为前面已有专门的变量存放这些信息);
而正常的代码,第二次调用GetDIBits()前所申请的内存空间,包括了BITMAPINFO、COLOR TABLE以及实际DIB数据的内容(将前面获得的BITMAPINFO、COLOR TABLE都拷贝到此空间)。
我郁闷啦,难道是这里出的问题?可是这样的话,函数为什么要把这些信息的地址分开两个参数来传送呢?
然后在微软的支持中心找到一篇文章,基本证实了这点http://support.microsoft.com/kb/80080/en-us
(这篇文章里讲了使用时的步骤,早点看见就好了。。。)
以下是更改后的代码。
BITMAPINFO Bmi;
HDC hBmpDC = ::GetDC(NULL);
Bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL FgRetVal = FALSE;
if (::GetDIBits(hBmpDC,hBmp,0,0,NULL,&Bmi,DIB_RGB_COLORS)) // 第一次调用,获取位图信息
{
DWORD InfoSize = 0;
InfoSize = Bmi.bmiHeader.biSize + Bmi.bmiHeader.biClrUsed * sizeof(RGBQUAD); // 位图信息及调色板大小
// 分配空间大小须包括位图信息、调色板、位图数据
BYTE *pTempBits = new BYTE [Bmi.bmiHeader.biSizeImage + InfoSize];
if(pTempBits)
{
memcpy(pTempBits,&Bmi,InfoSize); // 将位图信息及调色板大小拷贝至新的内存空间
if (::GetDIBits(hBmpDC, hBmp, 0, Bmi.bmiHeader.biHeight, pTempBits+InfoSize, (LPBITMAPINFO)pTempBits,
DIB_RGB_COLORS))
{
FgRetVal = TRUE;
}
}
}
不过对于确切的原因我还是不太清楚,估计是因为第二次调用GetDIBits()时,对参数LPVOID lpvBits前的内容进行了操作(因打开24色位图时正常,所以可能是对COLOR TABLE的操作),希望看见这篇文章且又清楚原因的高手能指点一下!
微软也太不厚道了,对于调用这个函数,参数传递要注意的基本问题,居然不在MSDN中说清楚,害得我瞎折腾了几天~ ~