2

I want to display a Bitmap in an Image container with WPF.

private void updateImagePreview()
{
    Bitmap bmp = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width,
                            Screen.PrimaryScreen.WorkingArea.Height);
    Graphics gr = Graphics.FromImage(bmp);
    Image Image_Preview;

    while (true)
    {
        gr.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(bmp.Width, bmp.Height));
        Image_Preview.Source = loadBitmap(bmp);
    }
}

[DllImport("gdi32")]
static extern int DeleteObject(IntPtr o);
public BitmapSource loadBitmap(System.Drawing.Bitmap source)
{
    IntPtr ip = source.GetHbitmap();
    BitmapSource bs = null;
    try
    {
        bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                 ip, IntPtr.Zero, Int32Rect.Empty,
                 System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(ip);
    }

    return bs;
}

The problem is, that this creates a huge memory leak. The leak occurs on the CreateBitmapSourceFromHBitmap call and fills memory while looping till the limit is exceeded. If I don't use that call, then the leak disappears. Any idea why it happens?

CharithJ
  • 46,289
  • 20
  • 116
  • 131
Flupp
  • 856
  • 10
  • 24
  • What do you mean with "memory leak"? It keeps references in memory after it should've been destroyed? Or does a single call fill up your memory? – almulo Jul 24 '15 at 09:43
  • It keeps references. A single call would work, but the memory leak would be still there. – Flupp Jul 24 '15 at 09:45
  • Did you see [this exact same question](http://stackoverflow.com/q/28952654/1136211)? It has a lot of links, but yet no answer. – Clemens Jul 24 '15 at 10:01
  • 1
    Try an alternative approach: save the bitmap to a MemoryStream and decode a BitmapImage or BitmapFrame from that stream (as shown in multiple posts on StackOverflow) – Clemens Jul 24 '15 at 10:04
  • I tried the approach [here](http://code.logos.com/blog/2008/04/memory_leak_with_bitmapimage_and_memorystream.html), including the wrapper class and it's also leaking memory. – Flupp Jul 24 '15 at 10:28
  • How about [`BitmapFrame.Create`](https://msdn.microsoft.com/en-us/library/ms615998(v=vs.110).aspx) with `OnLoad` set? – Clemens Jul 24 '15 at 10:33
  • Tried it, didn't stop leaking either. – Flupp Jul 24 '15 at 10:42
  • what is the image size here? –  Jul 24 '15 at 12:55
  • Tested with 1920x1080 px. – Flupp Jul 24 '15 at 16:38

3 Answers3

1

Try calling Freeze() on your BitmapSource before returning it. It seems to help release some references to the image bytes.

[DllImport("gdi32")]
static extern int DeleteObject(IntPtr o);
public BitmapSource loadBitmap(System.Drawing.Bitmap source)
{
    IntPtr ip = source.GetHbitmap();
    BitmapSource bs = null;
    try
    {
        bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                 ip, IntPtr.Zero, Int32Rect.Empty,
                 System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(ip);
    }

    bs.Freeze();
    return bs;
}
almulo
  • 4,918
  • 1
  • 20
  • 29
0

If you have a memory leak, the first thing that you should check is, if there are any Disposable objects that are not disposed.

I can see that you don't dispose both Image and Graphics instances, that could cause memory leaks.

Make sure you dispose them at the end of their usage.

Eg: In updateImagePreview method

using (Graphics gr = Graphics.FromImage(bmp))
{
   ....
}

Eg: In loadBitmap method

public BitmapSource loadBitmap(System.Drawing.Bitmap source)
{
    ....

    finally
    {
        DeleteObject(ip);
        source.Dispose();
    }

    return bs;
}
CharithJ
  • 46,289
  • 20
  • 116
  • 131
0

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;
    }
Flupp
  • 856
  • 10
  • 24