最近一直在调试第三方委托开发的医疗输液系统(我接手时,代码已经完成,原则上我只修改接口部分以适应我们的硬件即可,不过调试过程中,该程序本身问题暴露不少),该系统用VB.net开发,该软件的图形界面是花费n多银子专门做的美工,大量的贴图,就是目前的主流PC机配置,也很难在调试模式下走顺溜。
问题出在两方面,第一、存在内存泄露(有时间我专门为此写篇文章),第二、存在GDI泄露。
最早的时候由于存在内存泄露问题,我一直以为windows弹出的“GDI一般性错误”是由于内存泄露引起的,直到内存问题得以解决,程序还是运行一段时间就弹出类似错误。查了很多资料,发现windows任务管理器可以查看GDI是否泄露,如下图(在查看菜单,选定“选择列”对话框中gdi对象选项)
发现,该程序的GDI对象计数猛涨,由于没有很好的GDI检查工具和方法(如果这方面有高手,希望不吝指教),所以我对程序中大段大段涉及GDI的代码进行屏蔽,然后再检查是否GDI有泄露,n次重复后,功夫不负有心人,终于锁定罪魁祸首,相关代码如下:
Select Case (uPumpData.PumpName & "").Trim
Case "******"
IRBDetail.Icon = Icon.FromHandle(CType(ImgTabPage.Images(1), Bitmap).GetHicon)
Case "XXXXXX"
IRBDetail.Icon = Icon.FromHandle(CType(ImgTabPage.Images(0), Bitmap).GetHicon)
Case Else
IRBDetail.Icon = Nothing
End Select
问题就出在 Icon.FromHandle(CType(ImgTabPage.Images(1), Bitmap).GetHicon)这句代码上,msdn在关键时刻不辱使命,一查结果真相大白:
Icon.FromHandle 方法
命名空间:System.Drawing
程序集:System.Drawing(在 system.drawing.dll 中)
Visual Basic(声明)
Visual Basic(用法)
Dim handle As IntPtr
参数
handle 图标的 Windows 句柄。
返回值
此方法创建的 Icon。
备注
使用此方法时,必须使用 Win32 API 中的 DestroyIcon 方法释放产生的图标以确保释放相应资源。
示例
下面的代码示例设计用于 Windows 窗体,它需要 PaintEventArgse(这是 Paint 事件处理程序的参数)。代码执行下列操作:
· 将该对象绘制到屏幕。
· 获取 Bitmap 的图标句柄。
· 将窗体的 Form.Icon 属性设置为从该句柄创建的图标。
· 调用 Win32 API 函数 DestroyIcon 以释放资源。
<System.Runtime.InteropServices.DllImportAttribute("user32.dll")> _
Private Shared Function DestroyIcon(ByVal handle As IntPtr) As Boolean
End Function
Private Sub GetHicon_Example(ByVal e As PaintEventArgs)
' Create a Bitmap object from an image file.
Dim myBitmap As New Bitmap("c:/FakePhoto.jpg")
' Draw myBitmap to the screen.
e.Graphics.DrawImage(myBitmap, 0, 0)
' Get an Hicon for myBitmap.
Dim HIcon As IntPtr = myBitmap.GetHicon()
' Create a new icon from the handle.
Dim newIcon as Icon = System.Drawing.Icon.FromHandle(HIcon)
' Set the form Icon attribute to the new icon.
Me.Icon = newIcon
' Destroy the icon, since the form creates its
' own copy of the icon.
DestroyIcon(newIcon.Handle)
End Sub
注意,备注很关键:要用API DestroyIcon释放相关对象。
我声明了一个API函数:
Public Declare Function DestroyIcon Lib "user32" Alias "DestroyIcon" (ByVal hIcon As Integer) As Integer
在IRBDetail.Icon的属性代码中添加了如下代码,问题立马解决(当然类销毁的代码中,m_Icon也要释放一下)。
Public Property Icon() As Icon
Get
Return m_Icon
End Get
Set(ByVal Value As Icon)
'叶帆 2007.08.31
If Not IsNothing(m_Icon) Then
DestroyIcon(m_Icon.Handle)
m_Icon.Dispose()
m_Icon = Nothing
End If
m_Icon = Value
Me.Invalidate()
End Set
End Property
千里之堤溃于蚁穴,几万行的代码,就坏在这一点上,实在值得人警惕!