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

Win32API远程桌面服务器接收JPEG截图时抛出std::bad_alloc求助

问题分析与修复方案

核心问题点

  • recv错误未处理:当recv返回-1(网络错误)时,直接把负数加到bytesReceived里,导致bufferLenNetworkOrder读取错误,最终bufferLen变成非法值(超大正数或负数),new char[bufferLen]自然抛出std::bad_alloc。
  • 类型不匹配导致溢出:ntohll返回ULONGLONG类型,却赋值给int变量bufferLen。若截图大小解析后超出int范围,会溢出成负数,new负数大小直接触发异常。
  • MAX_BYTES检查太晚:拿到bufferLen后没有立刻判断合法性,而是先分配内存、接收数据,无效操作已经触发才终止。
  • WM_PAINT中做阻塞网络操作:WM_PAINT是窗口绘制消息,在此调用recv会阻塞窗口线程导致界面卡死,且每次触发WM_PAINT都接收数据,极易造成数据顺序混乱,后续bufferLen完全错误。
  • 重复分配内存:代码末尾imageBuffer.reset(new char[bufferLen])完全多余,既浪费内存又可能引发额外分配异常。
  • GlobalFree重复释放:CreateStreamOnHGlobal第二个参数传TRUE时,释放pStream会自动释放hGlobal,手动调用GlobalFree(hGlobal)会导致双重释放,引发未定义行为。

修复后的代码示例

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(Chwnd, &ps);

    // 注意:WM_PAINT仅负责绘制,网络接收需放在独立工作线程中!
    // 以下先修正内存分配与错误处理问题,线程部分需自行补充
    SOCKET selectedSocket = clientSockets[itemIndex];

    ULONGLONG bufferLenNetworkOrder = 0;
    int bytesReceived = 0;
    int rerr = 0;
    const ULONGLONG MAX_BYTES = 100000; // 改用ULONGLONG避免类型不匹配

    // 接收buffer长度,处理recv错误
    while (bytesReceived < sizeof(bufferLenNetworkOrder)) {
        rerr = recv(selectedSocket, (char*)&bufferLenNetworkOrder + bytesReceived, sizeof(bufferLenNetworkOrder) - bytesReceived, 0);
        if (rerr == SOCKET_ERROR) {
            MessageBoxA(NULL, "接收长度时网络错误", "错误", MB_OK);
            EndPaint(Chwnd, &ps);
            break;
        }
        bytesReceived += rerr;
    }

    ULONGLONG bufferLen = ntohll(bufferLenNetworkOrder); // 用ULONGLONG存储长度
    if (bufferLen > MAX_BYTES || bufferLen == 0) {
        MessageBoxA(NULL, "非法的图片长度", "错误", MB_OK);
        EndPaint(Chwnd, &ps);
        break;
    }

    std::unique_ptr<char[]> imageBuffer;
    try {
        imageBuffer.reset(new char[bufferLen]);
    } catch (const std::bad_alloc& e) {
        MessageBoxA(NULL, "内存分配失败", "错误", MB_OK);
        EndPaint(Chwnd, &ps);
        break;
    }

    bytesReceived = 0;
    while (bytesReceived < bufferLen) {
        rerr = recv(selectedSocket, imageBuffer.get() + bytesReceived, bufferLen - bytesReceived, 0);
        if (rerr == SOCKET_ERROR) {
            MessageBoxA(NULL, "接收图片数据时网络错误", "错误", MB_OK);
            EndPaint(Chwnd, &ps);
            break;
        }
        if (rerr == 0) {
            MessageBoxA(NULL, "客户端已断开", "提示", MB_OK);
            EndPaint(Chwnd, &ps);
            break;
        }
        bytesReceived += rerr;
    }

    // 接收完成,处理图片
    HGLOBAL hGlobal = GlobalAlloc(GHND, bufferLen);
    if (!hGlobal) {
        MessageBoxA(NULL, "全局内存分配失败", "错误", MB_OK);
        EndPaint(Chwnd, &ps);
        break;
    }

    void* pData = GlobalLock(hGlobal);
    memcpy(pData, imageBuffer.get(), bufferLen);
    GlobalUnlock(hGlobal);

    IStream* pStream = NULL;
    if (CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) != S_OK) {
        GlobalFree(hGlobal); // 创建流失败,手动释放内存
        MessageBoxA(NULL, "创建流失败", "错误", MB_OK);
        EndPaint(Chwnd, &ps);
        break;
    }

    Gdiplus::Bitmap bitmap(pStream);
    Gdiplus::Status status = bitmap.GetLastStatus();
    if (status != Gdiplus::Ok) {
        MessageBoxA(NULL, "图片解析失败", "错误", MB_OK);
        pStream->Release();
        EndPaint(Chwnd, &ps);
        break;
    }

    int imgWidth = bitmap.GetWidth();
    int imgHeight = bitmap.GetHeight();
    Gdiplus::Graphics graphics(hdc);

    RECT clientRect;
    GetClientRect(Chwnd, &clientRect);
    graphics.DrawImage(&bitmap, 0, 0, imgWidth, imgHeight);

    // 释放资源:pStream释放时会自动释放hGlobal
    pStream->Release();
    EndPaint(Chwnd, &ps);
    
    break;
}

关键额外建议

  • 迁移网络接收至独立线程:禁止在窗口消息处理函数中做阻塞网络操作,否则窗口会失去响应,且消息触发时机不可控易导致数据混乱。正确做法是开工作线程专门接收图片数据,接收到后通过PostMessage给窗口发绘制消息,WM_PAINT仅负责绘制已接收的图片。
  • 添加数据包校验:传输时可增加魔法数或校验位,确保bufferLen合法,避免网络丢包或同步错误导致的非法长度。
  • 用智能指针管理资源:比如std::unique_ptr管理动态内存,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 05:21:57