Wpf Createbitmapsourcefromhbitmap() Memory Leak

WPF CreateBitmapSourceFromHBitmap() memory leak

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.

Bitmap Graphics CreateBitmapSourceFromHBitmap memory leak

I solved it somehow, but I don't exactly know how I got rid of that leak. I assume that calling the code that causes the memory leak in a thread somehow fixes that problem. I highly recommend to use the stream wrapper mentioned in the comments, because using only a MemoryStream causes also a memory leak. Anyways, the following is the code that doesn't lead to the mentioned memory leak and works like a charm for me.

Timer takeScreen;

// This is a button to start the screen capturing
private void Button_Play_Click(object sender, RoutedEventArgs e)
{
int fps = 30;
takeScreen = new Timer(o => addNewImage(), null, 0, 1000 / fps);
}

private void addNewImage()
{
using (Bitmap bmp = new Bitmap(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height))
{
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(bmp.Width, bmp.Height));
Image_Preview.Dispatcher.Invoke(new Action(() => Image_Preview.Source = loadBitmap(bmp)));
}
}
}

public BitmapSource loadBitmap(System.Drawing.Bitmap source)
{
BitmapSource bmpf = null;

using (MemoryStream ms = new MemoryStream())
{
using (WrappingStream ws = new WrappingStream(ms))
{
source.Save(ws, System.Drawing.Imaging.ImageFormat.Bmp);
bmpf = BitmapFrame.Create(ws, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}

return bmpf;
}

Memory leak in Imaging.CreateBitmapSourceFromHBitmap

As you already know, you have to Dispose() screenBmp.

You are actually calling it by an using statement, so that should be fine, but I suspect the try/catch could interfere.

Do you have a chance to move the try/catch so that only the CopyFromScreen and CreateBitmapSourceFromHBitmap are surrounded?

From comments

Since only after that closing brace of the using statement you are sure that the screenBmp can be disposed, I'm forcing a GC collect there

GC.Collect(); 
return result;

and it doesn't seem leaking.

Here is my demo

class Program
{

[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
private static Screen SavedScreen { get; } = Screen.PrimaryScreen;

private static BitmapSource CopyScreen()
{
//try
//{
BitmapSource result;
using (
var screenBmp = new Bitmap(200, 100))
{
using (Graphics bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(SavedScreen.Bounds.X, SavedScreen.Bounds.Y, 0, 0, screenBmp.Size,
CopyPixelOperation.SourceCopy);
IntPtr hBitmap = screenBmp.GetHbitmap();
bmpGraphics.Dispose();
//********** Next line do memory leak
result = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
//result = null;

}
}
GC.Collect();
return result;
//}
//catch (Exception ex)
//{
// //ErrorReporting ($"Error in CopyScreen(): {ex}");
// Console.WriteLine(ex.Message);
// Debugger.Break();
// return null;
//}
}

static void Main(string[] args)
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(100);
var test = CopyScreen();
}
}
}

Memory leak, but where?

As far I can tell, there are no leaks going on. The problem is that you're allocating big C# objects fast and garbage collector kicks in way too late?

Here are few relevant topics:

Avoiding OutOfMemoryException during large, fast and frequent memory allocations in C#

and here is useful thread:

Garbage Collection not happening even when needed

If you kick GC.Collect(with generations 0..3), your memory consumption will be fixed:

    while (true)
{
Thread.Sleep(5);

using (var bitmap = new GDI.Bitmap(1000, 1000))
{
var hbitmap = bitmap.GetHbitmap();
var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());

image.Freeze();


DeleteObject(hbitmap);
}

Console.WriteLine("Current memory consumption" + GC.GetTotalMemory(false));
GC.Collect(3);
}

and output:

Current memory consumption156572
Current memory consumption156572
Current memory consumption156572
Current memory consumption156572

The real problem is that GC does not know about your unmanaged allocations, even if you free them. You need to add memory pressure and let the GC know about it:

 var width = 1000;
var height = 1000;

using (var bitmap = new GDI.Bitmap(width, height))
{
var hbitmap = bitmap.GetHbitmap();
var allocatedSize = width*height*4; // each pixel takes ~4 bytes?!
GC.AddMemoryPressure(allocatedSize);

var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());

image.Freeze();


DeleteObject(hbitmap);
GC.RemoveMemoryPressure(allocatedSize);
}

Letting GC know about underlying unmanaged memory helps to make sure GC kicks in at right places.

C# WPF BitmapSource Memory Leak?

So first off, you only have 52 cards. Just create the images up front and keep them around for the life of the application. It is a Black Jack game after all; it is safe to assume that each card will be needed at one point or another.

That said, there is an issue with creating BitmapSource objects from streams. The byte[] held by the stream is not being freed when the stream is disposed. See my own question here. The only reason I didn't vote to close as a duplicate is because I think you should really just create the cards once and be done with it instead of creating these images 10,000+ times.

Unmanaged Memory leak

You need to call DeleteObject(...) on your hBitmap. See: http://msdn.microsoft.com/en-us/library/1dz311e4.aspx

private BitmapSource BitmaptoBitmapsource(System.Drawing.Bitmap bitmap)
{
BitmapSource bms;
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapSizeOptions sizeOptions = BitmapSizeOptions.FromEmptyOptions();
bms = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap,
IntPtr.Zero, Int32Rect.Empty, sizeOptions);
bms.Freeze();

// NEW:
DeleteObject(hBitmap);

return bms;
}

Memory leak when loading Tiff frames into dynamically created Wpf Image controls

I've checked your sample application and I don't see any memory leak there. I've used 24bit lzw compressed tiff image from here. Objects are released after Garbage Collection as expected. I've added a third button forsing the GarbageCollection to occur immideatly: GC.Collect();

And here is the result: http://www.screencast.com/t/F5hhyTkAQ3e

When you add images they are added to the memory. When you click unload you remove any reference to them, but that doesn't mean that Garbage Collection will start immediatly. Objects are put in Large Objects Heap and Garbage Collection there is not happening as fast as for First Generation objects.
But when GC is done(for example, you can click add after unload, that might forse GC), the objects are successfully removed from memory.



Related Topics



Leave a reply



Submit