Search code examples
c#memorybitmapgraphic

C# Bitmap/Graphics Out of Memory


I'm trying to take a snapshot of the whole screen for reading pixel values. Actually i'm doing it without any problem. But after exactly 214 snapshots, i'm getting out of memory exception.

Bitmap ScreenShot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
  Screen.PrimaryScreen.Bounds.Height);

public Bitmap TakeSnapshot()
{
    Graphics graphic = null;
    Rectangle rect = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width,
      Screen.PrimaryScreen.Bounds.Height);

    using (graphic = Graphics.FromImage(ScreenShot))
    {
        graphic.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 
            0, 0, 
            ScreenShot.Size, 
            CopyPixelOperation.SourceCopy);
    }

    return ScreenShot.Clone(rect,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}

I'm using this method with timer

Bitmap bmp = TakeSnapshot();
        var c = bmp.GetPixel(0,0);

It was giving invalid parameter exception. I solved it with "using". But now i'm stuck on this exception.


Solution

  • You need to dispose disposable resources once you're done working with them. Bitmap class implements IDisposable - so it is disposable resource. Correct pattern is instead of

    Bitmap bmp = TakeSnapshot();
    var c = bmp.GetPixel(0,0);
    

    Something like

    Bitmap bmp = null;
    try
    {
      bmp = TakeSnapshot();
      var c = bmp.GetPixel(0,0);
      // any more work with bmp
    }
    finally
    {
      if (bmp != null)
      {
        bmp.Dipose();    
      }
    }
    

    Or in short form (which is preferable):

    using(Bitmap bmp = TakeSnapshot())
    {
      var c = bmp.GetPixel(0,0);
      // any more work with bmp
    }
    

    Reference: Using Objects That Implement IDisposable

    Edit

    You can easily emulate the issue:

    public class TestDispose : IDisposable
    {
        private IntPtr m_Chunk;
        private int m_Counter;
        private static int s_Counter;
    
        public TestDispose()
        {
            m_Counter = s_Counter++;
            // get 256 MB
            m_Chunk = Marshal.AllocHGlobal(1024 * 1024 * 256);
            Debug.WriteLine("TestDispose {0} constructor called.", m_Counter);
        }
    
        public void Dispose()
        {
            Debug.WriteLine("TestDispose {0} dispose called.", m_Counter);
            Marshal.FreeHGlobal(m_Chunk);
            m_Chunk = IntPtr.Zero;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            for(var i = 0; i < 1000; i ++)
            {
                var foo = new TestDispose();
            }
            Console.WriteLine("Press any key to end...");
            Console.In.ReadLine();
        }
    }