最近一直在調試第三方委托開發的醫療打點滴系統(我接手時,代碼已經完成,原則上我隻修改接口部分以适應我們的硬體即可,不過調試過程中,該程式本身問題暴露不少),該系統用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
千裡之堤潰于蟻穴,幾萬行的代碼,就壞在這一點上,實在值得人警惕!