You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下截图转字节数组生成BMP文件头错误,如何正确写入?

如何正确从字节数据写入BMP文件?

尝试使用C++和Windows API将截图数据转换为代表位图(Bitmap)图像的字节数组,然后保存到磁盘。保存后的图像无法在Windows照片查看器中打开,但可以在GIMP中打开且图像可见,不过GIMP会给出警告:gimp-image-set-resolution failed,resolution was out of range,so default resolution is used,推测位图的头数据复制存在问题。

以下是完整代码:

#include <iostream>
#include <windows.h>
#include <fstream>

void WINAPI CaptureScreenIntoByteArray(BYTE*& screen_bytes, DWORD& screen_bytes_size)
{
    BITMAPFILEHEADER bfHeader;
    BITMAPINFOHEADER biHeader;
    BITMAPINFO bInfo;
    HGDIOBJ hTempBitmap;
    HBITMAP hBitmap;
    BITMAP bAllDesktops;
    HDC hDC, hMemDC;
    LONG lWidth, lHeight;
    BYTE* sb = NULL;

    ZeroMemory(&bfHeader, sizeof(BITMAPFILEHEADER));
    ZeroMemory(&biHeader, sizeof(BITMAPFILEHEADER));
    ZeroMemory(&bInfo, sizeof(BITMAPINFO));
    ZeroMemory(&bAllDesktops, sizeof(BITMAP));

    hDC = GetDC(NULL);
    hTempBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
    GetObjectW(hTempBitmap, sizeof(BITMAP), &bAllDesktops);

    lWidth = bAllDesktops.bmWidth;
    lHeight = bAllDesktops.bmHeight;

    DeleteObject(hTempBitmap);

    bfHeader.bfType = (WORD)('B' | ('M' << 8));
    bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    biHeader.biSize = sizeof(BITMAPINFOHEADER);
    biHeader.biBitCount = 24;
    biHeader.biCompression = BI_RGB;
    biHeader.biPlanes = 1;
    biHeader.biWidth = lWidth;
    biHeader.biHeight = lHeight;

    bInfo.bmiHeader = biHeader;

    screen_bytes_size = (((24 * lWidth + 31) & ~31) / 8) * lHeight;

    hMemDC = CreateCompatibleDC(hDC);
    hBitmap = CreateDIBSection(hDC, &bInfo, DIB_RGB_COLORS, (VOID**)&sb, NULL, 0);
    SelectObject(hMemDC, hBitmap);

    int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
    BitBlt(hMemDC, 0, 0, lWidth, lHeight, hDC, x, y, SRCCOPY);

    // copy image data, i guess something is not correctly copied
    screen_bytes = new BYTE[sizeof(BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER) + screen_bytes_size];
    std::copy(&bfHeader, &bfHeader + 1, (BITMAPFILEHEADER*)screen_bytes);
    std::copy(&biHeader, &biHeader + 1, (BITMAPINFOHEADER*)(screen_bytes + sizeof(BITMAPFILEHEADER)));
    std::copy(sb, sb + screen_bytes_size, screen_bytes + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));

    DeleteDC(hMemDC);
    ReleaseDC(NULL, hDC);
    DeleteObject(hBitmap);
}
int main() {

    //get image data array
    BYTE* screen_bytes = NULL;
    DWORD screen_bytes_size = 0;
    CaptureScreenIntoByteArray(screen_bytes,screen_bytes_size);

    //save image data array as bmp file
    FILE* file = fopen( "output.bmp", "wb" );
    fwrite( screen_bytes, screen_bytes_size, 1, file );
    fclose(file);

   //do further work with image data array
    return 0;
}

问题分析与修正方案

代码存在多个导致文件格式错误的问题,逐一修正如下:

1. BITMAPINFOHEADER初始化大小错误

原代码中ZeroMemory(&biHeader, sizeof(BITMAPFILEHEADER))使用了BITMAPFILEHEADER的大小初始化BITMAPINFOHEADER,但BITMAPINFOHEADER(40字节)比BITMAPFILEHEADER(14字节)长,导致biHeader的后续字段(如分辨率相关的biXPelsPerMeter、biYPelsPerMeter)残留垃圾值,这是GIMP警告分辨率异常的原因。

修正:

ZeroMemory(&biHeader, sizeof(BITMAPINFOHEADER));

2. 未设置BITMAPFILEHEADER的bfSize字段

bfSize表示整个BMP文件的总字节数,Windows照片查看器依赖该字段识别文件完整性,必须设置为文件头+信息头+像素数据的总大小。

修正:

bfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + screen_bytes_size;

3. fwrite写入长度错误

原代码中fwrite仅写入了screen_bytes_size(仅像素数据大小),但screen_bytes数组包含文件头、信息头和像素数据三部分,导致写入的文件缺少部分头数据,Windows照片查看器无法识别。

修正:

DWORD total_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + screen_bytes_size;
fwrite(screen_bytes, total_size, 1, file);

4. 屏幕尺寸获取方法错误

原代码通过GetCurrentObject(hDC, OBJ_BITMAP)获取的是DC默认位图尺寸,并非实际屏幕分辨率,多显示器场景下会出错,应直接使用系统API获取虚拟屏幕尺寸。

修正:

lWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
lHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// 移除以下错误代码:
// hTempBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
// GetObjectW(hTempBitmap, sizeof(BITMAP), &bAllDesktops);
// DeleteObject(hTempBitmap);

修正后的完整代码

#include <iostream>
#include <windows.h>
#include <fstream>
#include <algorithm>

void WINAPI CaptureScreenIntoByteArray(BYTE*& screen_bytes, DWORD& screen_bytes_size)
{
    BITMAPFILEHEADER bfHeader;
    BITMAPINFOHEADER biHeader;
    BITMAPINFO bInfo;
    HBITMAP hBitmap;
    HDC hDC, hMemDC;
    LONG lWidth, lHeight;
    BYTE* sb = NULL;

    ZeroMemory(&bfHeader, sizeof(BITMAPFILEHEADER));
    ZeroMemory(&biHeader, sizeof(BITMAPINFOHEADER));
    ZeroMemory(&bInfo, sizeof(BITMAPINFO));

    hDC = GetDC(NULL);
    // 正确获取虚拟屏幕尺寸
    lWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    lHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);

    // 设置文件头
    bfHeader.bfType = (WORD)('B' | ('M' << 8));
    bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // 设置信息头
    biHeader.biSize = sizeof(BITMAPINFOHEADER);
    biHeader.biBitCount = 24;
    biHeader.biCompression = BI_RGB;
    biHeader.biPlanes = 1;
    biHeader.biWidth = lWidth;
    biHeader.biHeight = lHeight;
    // 初始化分辨率字段为0(或按需设置合理值)
    biHeader.biXPelsPerMeter = 0;
    biHeader.biYPelsPerMeter = 0;

    bInfo.bmiHeader = biHeader;

    // 计算像素数据大小(每行字节数按4字节对齐)
    screen_bytes_size = (((24 * lWidth + 31) & ~31) / 8) * lHeight;
    // 设置文件总大小
    bfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + screen_bytes_size;

    hMemDC = CreateCompatibleDC(hDC);
    hBitmap = CreateDIBSection(hDC, &bInfo, DIB_RGB_COLORS, (VOID**)&sb, NULL, 0);
    SelectObject(hMemDC, hBitmap);

    int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
    BitBlt(hMemDC, 0, 0, lWidth, lHeight, hDC, x, y, SRCCOPY);

    // 分配完整的字节数组空间
    screen_bytes = new BYTE[bfHeader.bfSize];
    // 复制文件头
    std::copy(reinterpret_cast<BYTE*>(&bfHeader), 
              reinterpret_cast<BYTE*>(&bfHeader) + sizeof(BITMAPFILEHEADER), 
              screen_bytes);
    // 复制信息头
    std::copy(reinterpret_cast<BYTE*>(&biHeader), 
              reinterpret_cast<BYTE*>(&biHeader) + sizeof(BITMAPINFOHEADER), 
              screen_bytes + sizeof(BITMAPFILEHEADER));
    // 复制像素数据
    std::copy(sb, sb + screen_bytes_size, 
              screen_bytes + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));

    DeleteDC(hMemDC);
    ReleaseDC(NULL, hDC);
    DeleteObject(hBitmap);
}

int main() {
    BYTE* screen_bytes = NULL;
    DWORD screen_bytes_size = 0;
    CaptureScreenIntoByteArray(screen_bytes, screen_bytes_size);

    FILE* file = fopen("output.bmp", "wb");
    if (file != NULL) {
        // 写入完整的文件数据
        DWORD total_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + screen_bytes_size;
        fwrite(screen_bytes, total_size, 1, file);
        fclose(file);
    }

    // 释放内存
    delete[] screen_bytes;
    return 0;
}

内容的提问来源于stack exchange,提问作者Brandonn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:03:10