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

数万次调用DrawTrainData后GetDC返回NULL的问题排查

问题:对话框应用中DrawTrainData函数调用数万次后::GetDC返回NULL的原因

问题背景

基于对话框的应用中,以下DrawTrainData函数被调用数万次后出现错误,排查发现::GetDC(this->GetSafeHwnd())返回NULL,请问问题出在哪里?

void CBitmapCtrl::DrawTrainData(Matrix* Imgs)
{
  if (Imgs->size() > 0 && m_cropWidth != 0 && m_cropHeight != 0)
  {
    struct bitmap_img
    {
      BITMAPINFO bmi;
      unsigned char img[29 * 27 * 3];
    } bitmap_img;
    for (auto n = 0; n < Imgs->size(); n++)
    {
                   // 생성된 이미지 데이터를 RGB각각 복사..
      for (auto i = 0; i < m_cropWidth * m_cropHeight * 3; i += 3)
      {
        bitmap_img.img[i] = static_cast<unsigned char>((*(*Imgs)[n])[i]);
        bitmap_img.img[i + 1] = static_cast<unsigned char>((*(*Imgs)[n])[i + 1]);
        bitmap_img.img[i + 2] = static_cast<unsigned char>((*(*Imgs)[n])[i + 2]);
      }

      BITMAPINFO bmi;
      ZeroMemory(&bmi, sizeof(bmi));
      bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      bmi.bmiHeader.biWidth = m_cropWidth;
      bmi.bmiHeader.biHeight = -m_cropHeight;
      bmi.bmiHeader.biPlanes = 1;
      bmi.bmiHeader.biBitCount = 24;
      bmi.bmiHeader.biCompression = BI_RGB;
      bitmap_img.bmi = bmi;
      CRect r((n % 10) * m_cropWidth, (n / 10) * m_cropHeight, (n % 10) * m_cropWidth + m_cropWidth, (n / 10) * m_cropHeight + m_cropHeight);

    }
    CBitmap bmp;
    CDC digitDC;
    digitDC.CreateCompatibleDC(&memDC);
    HDC hdc = ::GetDC(this->GetSafeHwnd());
    HBITMAP hbmp = CreateDIBBitmap(hdc,&(bmi.bmiHeader), CBM_INIT, bitmap_img.img, &bmi, DIB_RGB_COLORS);
    bmp.m_hObject = hbmp;
    CBitmap* pOldBitmap = digitDC.SelectObject(&bmp);
    memDC.StretchBlt(r.left,r.top,m_cropWidth*3,m_cropHeight*3,&digitDC,0,0,m_cropWidth,m_cropHeight,SRCCOPY);
    digitDC.SelectObject(pOldBitmap);
    digitDC.DeleteDC();
  }
  Invalidate();
}

原因分析

核心问题是GDI资源泄漏,具体问题点如下:

  • 未释放GetDC获取的HDC:每次调用::GetDC获取窗口DC后,没有调用::ReleaseDC释放。Windows系统对每个进程的GDI资源数量有上限,数万次调用后资源被耗尽,导致::GetDC无法分配新的DC,最终返回NULL。
  • HBITMAP资源管理不当:CreateDIBBitmap创建的HBITMAP直接赋值给bmp.m_hObject,若CBitmap对象之前已有关联的位图,会导致旧HBITMAP泄漏;即使是首次赋值,这种直接操作成员变量的方式也不符合MFC资源管理规范,存在泄漏风险。
  • 额外逻辑问题:循环内定义的CRect r在循环结束后仅保留最后一次循环的值,后续StretchBlt使用的是这个最终值,属于逻辑错误,但不是导致当前崩溃的直接原因。

修复方案

  1. 必须释放GetDC获取的DC:
    在使用完hdc后,立即调用::ReleaseDC释放资源:
    HDC hdc = ::GetDC(this->GetSafeHwnd());
    if (hdc != NULL)
    {
        HBITMAP hbmp = CreateDIBBitmap(hdc,&(bmi.bmiHeader), CBM_INIT, bitmap_img.img, &bmi, DIB_RGB_COLORS);
        // 执行后续位图操作
        ::ReleaseDC(this->GetSafeHwnd(), hdc); // 释放DC资源
    }
    
  2. 规范管理HBITMAP资源:
    使用MFC的Attach方法关联HBITMAP,确保旧资源被正确清理:
    if (bmp.GetSafeHandle() != NULL)
    {
        bmp.DeleteObject();
    }
    bmp.Attach(hbmp);
    
  3. 添加返回值检查:
    检查CreateDIBBitmap的返回值,避免使用无效的HBITMAP导致后续操作出错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:25:28