【问题】GDI泄漏

来源:互联网 发布:学而思招聘java 编辑:程序博客网 时间:2024/05/22 01:58

问题

MFC程序,运行一段时间后,画面刷新异常。

现象1:画面刷新异常,花屏

这里写图片描述

现象2:死机,画面不再响应鼠标事件

现象3:CResourceException 异常,显示资源被耗尽

First-chance exception at 0x756dc54f in 机器视觉检测系统.exe: Microsoft C++ exception: CResourceException at memory location 0x0035e738..

CResourceException异常,是GDI泄漏的明显标志。我们打开Windows任务管理器,查看某个进程的GDI使用情况。
任务管理器默认状态下不显示GDI对象列,需要设置一下。

这里写图片描述

这里写图片描述
可以看到当前的应用程序GDI值已经达到9999(Windows默认10000)。重启应用程序,可以看到这个值一直增加,
可以通过这个方法来检测GDI是否存在泄漏的问题。GDI对象值是否只增不减。

也可以使用一些专用的GDI检测工具来检测。GDIndicator,GdiLeakDetector等。
参考:GDI 泄漏检测方法(http://www.cnblogs.com/doudongchun/p/3699695.html)

原因

我们的应用程序需要显示相机和电机的运行状态。大致是下面这个样子。
我们的实现方法是用CStatic的对象,加上一个1秒定时器,即每隔1秒刷新状态。

...m_MotorRunStatus.SetIcon(m_hIconGreen);...

其中MotorRunStatus是CStatic对象,hIconGreen是一个HICON对象。
上面的代码就是GDI泄漏的罪魁祸首。关键是每秒刷新,会导致GDI对象增加。

这里写图片描述

修改方法

我曾将尝试使用对象的ShowWindow方法,GDI对象依然会只增不减。(至今不明白为什么SetIcon和ShowWindow会导致GDI泄漏,用法错误??)
所以我的解决方法是暂时去掉刷新。直接用画刷描画,当然画刷也是一种GDI对象,使用后一定记得释放。

解决方法2:修改GDI默认值
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota
当前默认值为10000,如果每一秒刷新一次的话,大概2小时46分作用GDI对象被耗尽,产生CResourceException异常,画面无法正常刷新。与我们的现象完全吻合。
MSDN上说GDI可设置的范围是255~65535,按照这个数值计算,18小时后也将耗尽GDI对象。
MSDN:https://msdn.microsoft.com/en-us/library/windows/desktop/ms724291(v=vs.85).aspx

这里写图片描述

画面最小化时,画面不会刷新,所以即使泄漏,GDI值也是不会变化的。

参考:
关于应用程序出现窗口不完整,GDI对象猛增,GDI资源泄漏的问题的探讨
GDI Objects
彻底解决GDI对象泄漏的问题
GDI 泄漏检测方法
GDI泄漏和内存泄漏 检测方法
Windows内核对象、用户对象、GDI对象

0 0
原创粉丝点击