Windows API BitBlt在部分分辨率下截图失败问题求助
屏幕捕获程序特定分辨率下内存错误排查
问题描述
使用C++基于Windows API的BitBlt函数编写了屏幕捕获保存程序,但部分分辨率下会触发内存错误,生成的截图无法查看,直接访问字节缓冲区时会抛出内存不可访问异常。已知失效分辨率包括:
- 所有宽高小于36×36的情况
- 255×255的分辨率
问题根源
核心问题是Windows DIB(设备无关位图)的行对齐要求:DIB规定每一行的字节数必须是4的整数倍,即行宽需按4字节对齐。你的代码直接用width * 3计算每行字节数,完全忽略了对齐规则,导致以下问题:
- 35×35分辨率:每行实际需要108字节(35×3=105,向上取整到最近的4的倍数),但代码按105字节计算总数据量,拷贝时要么未获取完整数据,要么访问超出自己分配的内存触发异常。
- 255×255分辨率:255×3=765,765除以4余1,实际行宽为768字节,总数据量远大于代码计算的
255×255×3,拷贝时必然出现内存越界。
另外还有个次要问题:代码中保存的文件后缀为.png,但实际写入的是BMP格式,导致系统无法正确识别文件。
修复后的代码
#include <iostream> #include <windows.h> #include <fstream> // 计算4字节对齐后的行字节数 inline int GetAlignedRowWidth(int width, int bitsPerPixel) { return ((width * bitsPerPixel + 31) / 32) * 4; } BYTE* CaptureScreen(int x, int y, int width, int height, int alignedRowWidth); bool CaptureScreenAndSaveToFile(int x, int y, int width, int height, const char* filename) { const int bitsPerPixel = 24; const int alignedRowWidth = GetAlignedRowWidth(width, bitsPerPixel); const int totalDataSize = alignedRowWidth * height; BYTE* imageData = CaptureScreen(x, y, width, height, alignedRowWidth); if (imageData == nullptr) { return false; } std::ofstream outFile(filename, std::ios::binary); if (!outFile.is_open()) { delete[] imageData; return false; } // 写入BMP文件头 BITMAPFILEHEADER bmpFileHeader; bmpFileHeader.bfType = 0x4D42; // 'BM'标识 bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + totalDataSize; bmpFileHeader.bfReserved1 = 0; bmpFileHeader.bfReserved2 = 0; bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); outFile.write(reinterpret_cast<const char*>(&bmpFileHeader), sizeof(BITMAPFILEHEADER)); // 写入BMP信息头 BITMAPINFOHEADER bmpInfoHeader; bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfoHeader.biWidth = width; bmpInfoHeader.biHeight = -height; // 负高度表示图像自上而下 bmpInfoHeader.biPlanes = 1; bmpInfoHeader.biBitCount = bitsPerPixel; bmpInfoHeader.biCompression = BI_RGB; bmpInfoHeader.biSizeImage = totalDataSize; // 明确填写对齐后的图像数据大小 bmpInfoHeader.biXPelsPerMeter = 0; bmpInfoHeader.biYPelsPerMeter = 0; bmpInfoHeader.biClrUsed = 0; bmpInfoHeader.biClrImportant = 0; outFile.write(reinterpret_cast<const char*>(&bmpInfoHeader), sizeof(BITMAPINFOHEADER)); // 写入对齐后的完整图像数据 outFile.write(reinterpret_cast<const char*>(imageData), totalDataSize); outFile.close(); delete[] imageData; return true; } BYTE* CaptureScreen(int x, int y, int width, int height, int alignedRowWidth) { BYTE* result = nullptr; const int totalDataSize = alignedRowWidth * height; HDC hdc = GetDC(HWND_DESKTOP); if (hdc == NULL) return result; HDC hdcTemp = CreateCompatibleDC(hdc); if (hdcTemp == NULL) { ReleaseDC(HWND_DESKTOP, hdc); return result; } BITMAPINFO bitmap; ZeroMemory(&bitmap, sizeof(bitmap)); bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader); bitmap.bmiHeader.biWidth = width; bitmap.bmiHeader.biHeight = -height; bitmap.bmiHeader.biPlanes = 1; bitmap.bmiHeader.biBitCount = 24; bitmap.bmiHeader.biCompression = BI_RGB; bitmap.bmiHeader.biSizeImage = totalDataSize; bitmap.bmiHeader.biClrUsed = 0; bitmap.bmiHeader.biClrImportant = 0; BYTE* bitPointer = nullptr; HBITMAP hBitmap = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)&bitPointer, NULL, NULL); if (hBitmap != NULL && bitPointer != nullptr) { HBITMAP hPrevBitmap = (HBITMAP)SelectObject(hdcTemp, hBitmap); BitBlt(hdcTemp, 0, 0, width, height, hdc, x, y, SRCCOPY); // 按对齐后的总大小分配内存并拷贝数据 result = new BYTE[totalDataSize]; if (result != nullptr) { memcpy(result, bitPointer, totalDataSize); } SelectObject(hdcTemp, hPrevBitmap); DeleteObject(hBitmap); } DeleteDC(hdcTemp); ReleaseDC(HWND_DESKTOP, hdc); return result; } int main() { // 修正文件后缀为.bmp,确保格式匹配 CaptureScreenAndSaveToFile(125, 125, 35, 35, "Hello.bmp"); CaptureScreenAndSaveToFile(0, 0, 255, 255, "255x255.bmp"); return 0; }
修复要点说明
- 添加行对齐计算:
GetAlignedRowWidth函数使用标准公式计算符合DIB要求的行宽,确保每行字节数为4的整数倍。 - 修正内存逻辑:所有涉及图像数据大小的计算、内存分配和拷贝操作,均使用对齐后的总数据量,彻底避免内存越界。
- 明确图像数据大小:在
BITMAPINFO和BITMAPINFOHEADER中明确填写biSizeImage,让CreateDIBSection能准确分配内存。 - 修正文件后缀:将
.png改为.bmp,保证文件格式与内容一致,可被系统正确识别。
内容的提问来源于stack exchange,提问作者hopes32_
相关产品推荐
相关产品推荐

