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

C++中BYTE*数组转JPEG图像(libjpeg)跨设备异常求助

问题描述

开发一款将截图存储到外部服务器的应用,受资源限制需将截图以BYTE*数组的文本形式存储。本地会话内可正常读写截图,但朋友上传的截图会生成0KB空文件,且在扫描行函数执行时导致程序崩溃。

截图获取函数 SaveScreenshotNew

LPBYTE SaveScreenshotNew()
{
    int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
    int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);

    HDC hdcScreen = GetDC(NULL);
    HDC hdcMem = CreateCompatibleDC(hdcScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight);
    SelectObject(hdcMem, hBitmap);
    BitBlt(hdcMem, 0, 0, nScreenWidth, nScreenHeight, hdcScreen, 0, 0, SRCCOPY);

    int bufferSize = nScreenWidth * nScreenHeight * 3;
    LPBYTE buffer = new BYTE[bufferSize];

    BITMAPINFOHEADER bi;
    memset(&bi, 0, sizeof(BITMAPINFOHEADER));
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = nScreenWidth;
    bi.biHeight = -nScreenHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 24;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;

    if (GetDIBits(hdcScreen, hBitmap, 0, nScreenHeight, buffer, (BITMAPINFO*)&bi, DIB_RGB_COLORS) != 0) {
        for (int i = 0; i < nScreenHeight; i++) {
            for (int j = 0; j < nScreenWidth; j++) {
                int index = i * nScreenWidth * 3 + j * 3;
                BYTE blue = buffer[index];
                buffer[index] = buffer[index + 2];
                buffer[index + 2] = blue;
            }
        }
    }

    DeleteObject(hBitmap);
    DeleteDC(hdcMem);
    ReleaseDC(NULL, hdcScreen);

    return buffer;
}

libjpeg压缩代码

FILE* outfile = fopen(szPath, "wb");

struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);

jpeg_stdio_dest(&cinfo, outfile);

cinfo.image_width = sWidth;
cinfo.image_height = sHeight;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;

jpeg_set_defaults(&cinfo);
jpeg_start_compress(&cinfo, TRUE);

while (cinfo.next_scanline < cinfo.image_height) {
    JSAMPROW row_pointer = &gScreenPacket.lpData[cinfo.next_scanline * cinfo.image_width * cinfo.input_components];
    jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}

jpeg_finish_compress(&cinfo);

jpeg_destroy_compress(&cinfo);
fclose(outfile);

排查与修复建议

  • 修复BITMAP行对齐问题:Windows DIB位图要求行字节数为4的倍数,当前代码直接用nScreenWidth*3计算行大小,未考虑对齐,会导致缓冲区越界。修改缓冲区大小计算:

    int rowSize = ((nScreenWidth * 3 + 3) / 4) * 4; // 按4字节对齐计算行宽
    int bufferSize = rowSize * nScreenHeight;
    

    同时遍历像素时用对齐后的行大小:

    int index = i * rowSize + j * 3;
    
  • 同步跨机器分辨率参数:朋友机器的屏幕分辨率可能与本地不同,若sWidth/sHeight未正确获取对方实际分辨率,会导致jpeg压缩时访问非法内存。传输截图时必须同步分辨率参数,确保压缩时的尺寸与截图生成时一致。

  • 检查缓冲区传输完整性:BYTE*转文本传输时,可能存在编码错误或数据截断,导致接收端缓冲区不完整。传输前需记录缓冲区总大小,接收端先验证数据长度是否匹配,再执行解压操作。

  • 添加文件打开错误处理:当前代码未检查fopen是否成功,若路径无效或无写入权限,会生成0KB空文件。增加判断:

    if (outfile == NULL) {
        // 处理文件打开失败逻辑,如返回错误码
        return;
    }
    
  • 自定义libjpeg错误处理:默认错误处理会直接终止程序,自定义错误捕获逻辑避免崩溃:

    struct my_error_mgr {
        struct jpeg_error_mgr pub;
        jmp_buf setjmp_buffer;
    };
    
    METHODDEF(void) my_error_exit(j_common_ptr cinfo) {
        my_error_mgr* myerr = (my_error_mgr*)cinfo->err;
        longjmp(myerr->setjmp_buffer, 1);
    }
    
    // 使用时替换默认错误处理
    struct my_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_compress(&cinfo);
        fclose(outfile);
        // 处理错误逻辑
        return;
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:26:37