[C#] 當使用GetHbitmap繪製圖片時,釋放PictureBox所使用的記憶體

在前篇文章[Cognitive] 在C#中使用Emgu.CV進行簡易的人臉辨識中,遇到了一個棘手的問題
就是在程式碼執行的過程中,記憶體會不斷的增加,即使使用了PictureBox.Image = null; 或是PictureBox.Image.Dispose() 都沒有用
原來問題是出在GetHbitmap這個方法上面

在PictureBox放入圖片的方式中,可以透過圖檔路徑,或是圖片的串流資料將圖片放入到PictureBox控制項中,如下面的程式碼所示

// 透過圖檔路徑將圖片放入到PictureBox控制項中
PictureBox.Image = Image.FromFile("C\\TEST.jpg");

// 透過圖片的串流物件將圖片放入到PictureBox控制項中
PictureBox.Image = Image.FromStream(objStream);

上面放入圖片的作法,雖然說會增加主機記憶體的使用量,但是只要能夠將Image屬性指定為null,或是進行Dispose,就可以有效的釋放記憶體,避免記憶體的佔用,如下面的程式碼所示

// 如果圖片的屬性不為null, 就釋放掉Image屬性的物件,並釋放出記憶體的使用
if (PictureBox.Image != null)
{
  PictureBox.Image.Dispose();
}
但是如果在載入圖檔時所使用的方法是FromHbitmap(),那就完全不是這麼一回事了
不論是null,或是Dispose,對於釋放記憶都是完全沒有用處

為什麼?

簡單來說,因為bitmap是GDI+的物件,所以使用一般釋放記憶體的方式對於bitmap是無效的
要能夠成功的釋放GDI+物件所佔用的記憶體,就還是必須要使用Windows GDI元件,才能有效的釋放掉bitmap所使用的記憶體

使用的方式也很簡單,加上幾行程式碼就可以了

// 引用Windows GDI元件,並調用DeleteObject的方法
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

public void BindBitmapToPicture()
{
    // 將Bitmap物件轉換為平台指標
    IntPtr gdibitmap = myBitmap.Bitmap.GetHbitmap();
    // 將bitmap放入至PictureBox的Image屬性
    PictureBox.Image = Image.FromHbitmap(gdibitmap);
    // 進行Bitmap資源的釋放
    DeleteObject(gdibitmap);
}

這樣就可以很有效的釋放記憶體的使用了

參考資料:
Bitmap.GetHbitmap 方法 ()
About Bitmaps