Leonid Malyshev Leonid Malyshev - 4 months ago 97
C# Question

Memory leak in Imaging.CreateBitmapSourceFromHBitmap

I have next function (makes screenshot)

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

private BitmapSource CopyScreen()
{
try
{
BitmapSource result;
using (
var screenBmp = new Bitmap(SavedScreen.Bounds.Width, SavedScreen.Bounds.Height, PixelFormat.Format32bppArgb))
{
using (Graphics bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(SavedScreen.Bounds.X, SavedScreen.Bounds.Y, 0, 0, screenBmp.Size,
CopyPixelOperation.SourceCopy);
IntPtr hBitmap = screenBmp.GetHbitmap();

//********** Next line do memory leak
result = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
}
}
return result;
}
catch (Exception ex)
{
//ErrorReporting ($"Error in CopyScreen(): {ex}");
Debugger.Break();
return null;
}
}


And cannot avoid memory leak which is a result of calling
Imaging.CreateBitmapSourceFromHBitmap
. As I call this function in a cycle this memory leak is very important for me.
Called in WPF application (Windows, c#)

Answer

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();
        }
    }
}
Comments