CResourceException MFC程序GDI对象泄漏过程剖析

       优化程序之后发现程序运行一段时间后,就莫名奇妙的崩溃退出,并没有弹出任何信息。通过调试程序后发现CResourceException异常,通过网上查找才知道是GDI对象泄露。凡是CreateXX的,最后一定要DeleteObject或者DeleteXXX。

       打开任务管理器发现GDI对象不断增加,且数量到达9999后程序开始异常。因此判断程序可能存在GDI对象泄漏,即创建的GDI对象没有及时释放。鉴于GDI对象不断自动增加,判断GDI泄漏可能位于定时器触发的OnTimer函数中。OnTimer函数调用方法如下:

LRESULT CVServerView::OnChangeTime(WPARAM wParam, LPARAM lParam)
 {
     CString strText;
     FormatTime((INT)wParam, 30, strText);
     m_editPush.SetWindowText(strText);
     return 0;
 }

可以看出函数仅仅是调用了API函数SetWindowText,理论上不应该导致GDI泄漏。但奇怪的是注释掉SetWindowText后程序就正常了。后来发现原因在于程序重写了OnCtlColor函数。

HBRUSH CVServerView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
    if (pWnd->GetDlgCtrlID() == IDC_EDIT_PUSH) {
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 255));
        return HBRUSH(CreateSolidBrush(RGB(56, 56, 56)));
    }
    // 如果默认的不是所需画笔,则返回另一个画笔
    return hbr;
}

SetWindowText函数会调用以上OnCtlColor函数。OnCtlColor中CreateSolidBrush创建了GDI对象没有释放导致GDI对象不断增加,这就是问题所在。因此将程序修改为创建一个全局画笔m_brushBack=CreateSolidBrush(RGB(56, 56, 56)),OnDestroy时释放掉。修改代码如下:

HBRUSH CVServerView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
    if (pWnd->GetDlgCtrlID() == IDC_EDIT_PUSH) {
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 255));
        return HBRUSH(m_brushBack);
    }
    // 如果默认的不是所需画笔,则返回另一个画笔
    return hbr;
}

GDI泄漏会弹出CResourceException异常,一个过程的GDI上线是10000,若是跨越这个值法度就会

down掉。若是在视频处理惩罚函数存在gdi泄漏,会很轻易达到这个上限值。一个简单的办法可以断定gdi是否存在泄漏,就是应用Windows任务管理器中进程(【查看】-》【选择列】选上【GDI对象】)看里面的该过程的GDI对象是否一向在增长。

使用资源的对象和对象使用资源的方式。

1.使用资源的对象可分为两类 局部和全局

2.使用资源的方式也分为两类 拷贝和指针

结合你的代码,CreateCaret创建提示符 虽然没有明确使用资源的是什么对象 但是去MSDN上就可以知道了 实际上使用提示符的是CTextView这个窗口,是一个全局的对象。但是它使用资源的方式却可以认为是指针式的 即它在使用bitmap作为提示符时 其实并没有把bitmap拷贝一份 而是“寄希望于”它一直存在。这样 当你的bitmap是局部变量的时候 它就找不到这个资源了 也就没法显示提示符了。

但是当CDC* pDC; 使用CBrush,CPen,CFont时,pDC所指向的CDC是全局的 但是它使用资源的方式可以认为是拷贝式的 也就是当我们建立一个CPen pen; 并执行pDC->SelectObject(&pen);后,实际上相当于将pen拷贝了一份给CDC。这样 就算是pen是局部变量 在以后的使用过程中也会保持pen所代表的画笔。既然如此 我想如果使用资源的对象是局部的话,也很容易解释了,就不多说了。我对这个方面没有更深的理解了,上面也只是我自己按照自己的想法说的,楼主要找更加确切的东西,再问问高人 或者到MSDN上

去找几个资源使用的例子自己总结一下。另外去参考一下:

 http://dev.21tx.com/2004/02/14/17312.html(http://dev.21tx.com/2004/02/14/17312.html)上面这个文章当中提到了一个CGdiObject类的“虚拟析构函数”不知道是不是因为跟这个虚拟构造函数有关系(可能是基类没有实现这个析构函数)另外 GDI的很多麻烦在GDI+中有很多已经没有了。GDI+中很多以前GDI中需要清理内存的地方都没有了。GDI+是GDI的升级 学习方向应该向GDI+偏移。

因为brush是CBrush类,作为局部变量,出了作用域之后要销毁,而在CBrush的析构函数中,调用了DeleteObject(),所有其画刷句柄无效,指向的GDI对象被释放了;而hBrush没有删除GDI对象,虽然出了作用域之后,hBrush无效,但是其指向的GDI对象资源仍然存在。

OnCtlColor函数中返回画刷的销毁问题。

将hbr设置成成员变量,在构造函数中hbr = NULL 在析构函数中DeleteObject(hbr)并在OnCtlColor中这样写:

if (m_hbr)
{
     DeleteObject(m_hbr);
     m_hbr = CreateSolidBrush(g_pPublicClass->m_CurveSet.CurveHisColor);
}
else
{
     m_hbr = CreateSolidBrush(g_pPublicClass->m_CurveSet.CurveHisColor);
}
return  m_hbr;
}

猜你喜欢

转载自blog.csdn.net/woniulx2014/article/details/83245183