WPF CreateBitmapSourceFromHBitmap() memory leak

C#.NetWpfMemory Leaks

C# Problem Overview


I need to draw an image pixel by pixel and display it inside a WPF. I am attempting to do this by using a System.Drawing.Bitmap then using CreateBitmapSourceFromHBitmap() to create a BitmapSource for a WPF Image control. I have a memory leak somewhere because when the CreateBitmapSourceFromBitmap() is called repeatedly the memory usage goes up and does not drop off until the application is ended. If I don't call CreateBitmapSourceFromBitmap() there is no noticeable change in memory usage.

for (int i = 0; i < 100; i++)
{
    var bmp = new System.Drawing.Bitmap(1000, 1000);
    var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
        System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    source = null;
    bmp.Dispose();
    bmp = null;
}

What can I do to free the BitmapSource memory?

C# Solutions


Solution 1 - C#

MSDN for Bitmap.GetHbitmap() states:

> Remarks > > You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.

So use the following code:

// at class level
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

// your code
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(1000, 1000)) 
{
    IntPtr hBitmap = bmp.GetHbitmap(); 

    try 
    {
        var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    }
    finally 
    {
        DeleteObject(hBitmap);
    }
}

I also replaced your Dispose() call by an using statement.

Solution 2 - C#

Whenever dealing with unmanaged handles it can be a good idea to use the "safe handle" wrappers:

public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
	[SecurityCritical]
	public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
		: base(ownsHandle)
	{
		SetHandle(preexistingHandle);
	}

	protected override bool ReleaseHandle()
	{
		return GdiNative.DeleteObject(handle) > 0;
	}
}

Construct one like so as soon as you surface a handle (ideally your APIs would never expose IntPtr, they would always return safe handles):

IntPtr hbitmap = bitmap.GetHbitmap();
var handle = new SafeHBitmapHandle(hbitmap , true);

And use it like so:

using (handle)
{
  ... Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(), ...)
}

The SafeHandle base gives you an automatic disposable/finalizer pattern, all you need to do is override the ReleaseHandle method.

Solution 3 - C#

I had the same requirement and issue (memory leak). I implemented the same solution as marked as answer. But although the solution works, it caused an unacceptable hit to performance. Running on a i7, my test app saw a steady 30-40% CPU, 200-400MB RAM increases and the garbage collector was running almost every millisecond.

Since I'm doing video processing, I'm in need of much better performance. I came up with the following, so thought I would share.

Reusable Global Objects

//set up your Bitmap and WritableBitmap as you see fit
Bitmap colorBitmap = new Bitmap(..);
WriteableBitmap colorWB = new WriteableBitmap(..);

//choose appropriate bytes as per your pixel format, I'll cheat here an just pick 4
int bytesPerPixel = 4;

//rectangles will be used to identify what bits change
Rectangle colorBitmapRectangle = new Rectangle(0, 0, colorBitmap.Width, colorBitmap.Height);
Int32Rect colorBitmapInt32Rect = new Int32Rect(0, 0, colorWB.PixelWidth, colorWB.PixelHeight);

Conversion Code

private void ConvertBitmapToWritableBitmap()
{
    BitmapData data = colorBitmap.LockBits(colorBitmapRectangle, ImageLockMode.WriteOnly, colorBitmap.PixelFormat);

    colorWB.WritePixels(colorBitmapInt32Rect, data.Scan0, data.Width * data.Height * bytesPerPixel, data.Stride);

    colorBitmap.UnlockBits(data); 
}

Implementation Example

//do stuff to your bitmap
ConvertBitmapToWritableBitmap();
Image.Source = colorWB;

The result is a steady 10-13% CPU, 70-150MB RAM, and the garbage collector only ran twice in a 6 minute run.

Solution 4 - C#

This is a great(!!) post, although with all the comments and suggestions, it took me an hour to figure out the pieces. So here is a call to get the BitMapSource with SafeHandles, and then an example usage of it to create a .PNG image file. At the very bottom are the 'usings' and some of the references. Of course, none of the credit is mine, I am just the scribe.

private static BitmapSource CopyScreen()
{
	var left = Screen.AllScreens.Min(screen => screen.Bounds.X);
	var top = Screen.AllScreens.Min(screen => screen.Bounds.Y);
	var right = Screen.AllScreens.Max(screen => screen.Bounds.X + screen.Bounds.Width);
	var bottom = Screen.AllScreens.Max(screen => screen.Bounds.Y + screen.Bounds.Height);
	var width = right - left;
	var height = bottom - top;

	using (var screenBmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
	{
		BitmapSource bms = null;

		using (var bmpGraphics = Graphics.FromImage(screenBmp))
		{
			IntPtr hBitmap = new IntPtr();
			var handleBitmap = new SafeHBitmapHandle(hBitmap, true);

			try
			{
				bmpGraphics.CopyFromScreen(left, top, 0, 0, new System.Drawing.Size(width, height));

				hBitmap = screenBmp.GetHbitmap();

				using (handleBitmap)
				{
					bms = Imaging.CreateBitmapSourceFromHBitmap(
						hBitmap,
						IntPtr.Zero,
						Int32Rect.Empty,
						BitmapSizeOptions.FromEmptyOptions());

				} // using

				return bms;
			}
			catch (Exception ex)
			{
				throw new ApplicationException($"Cannot CopyFromScreen. Err={ex}");
			}

		} // using bmpGraphics
	}   // using screen bitmap
} // method CopyScreen

Here is the usage, and also the "Safe Handle" class:

private void buttonTestScreenCapture_Click(object sender, EventArgs e)
{
	try
	{
		BitmapSource bms = CopyScreen();
		BitmapFrame bmf = BitmapFrame.Create(bms);

		PngBitmapEncoder encoder = new PngBitmapEncoder();
		encoder.Frames.Add(bmf);

		string filepath = @"e:\(test)\test.png";
		using (Stream stm = File.Create(filepath))
		{
			encoder.Save(stm);
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show($"Err={ex}");
	}
}
    
public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    [System.Runtime.InteropServices.DllImport("gdi32.dll")]
    public static extern int DeleteObject(IntPtr hObject);

    [SecurityCritical]
    public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
        : base(ownsHandle)
    {
        SetHandle(preexistingHandle);
    }

    protected override bool ReleaseHandle()
    {
        return DeleteObject(handle) > 0;
    }
}

And finally a look at my 'usings':

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using System.Windows;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Security;

The DLLs referenced included:

  • PresentationCore
  • System.Core
  • System.Deployment
  • System.Drawing
  • WindowsBase

Solution 5 - C#

In my case it did not work directly with this method. I had to add a clean garbage collector in addition

    using (PaintMap p = new PaintMap())
    {
        System.Drawing.Image i = p.AddLineToMap("1");
        System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(i, 8419, 5953);
        IntPtr hBitmap = bmp.GetHbitmap();
            
        var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
        Image2.ImageSource = bitmapSource;
            
        DeleteObject(hBitmap);
            
        System.GC.Collect();
    }

Solution 6 - C#

I have a solution for someone who want to load image from memory or other class

 public static InteropBitmap Bitmap2BitmapImage(Bitmap bitmap)
        {
            try
            {
                var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
           
                return (InteropBitmap)source;

            }
            catch (Exception e)
            {
                MessageBox.Show("Convertion exception: " + e.Message + "\n" +e.StackTrace);
                return null;
            }
        }

And then I use it the set the source of an image

CurrentImage.Source = ImageConverter.Bitmap2BitmapImage(cam.Bitmap);

Image is the following definition

<Image x:Name="CurrentImage" Margin="5" StretchDirection="Both"
                Width="{Binding Width}"
                Height="{Binding Height}">
                </Image>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMr BellView Question on Stackoverflow
Solution 1 - C#Julien LebosquainView Answer on Stackoverflow
Solution 2 - C#Jack UklejaView Answer on Stackoverflow
Solution 3 - C#TrickySituationView Answer on Stackoverflow
Solution 4 - C#batpoxView Answer on Stackoverflow
Solution 5 - C#Yannick TurbangView Answer on Stackoverflow
Solution 6 - C#Lionel PeetersView Answer on Stackoverflow