c# picturebox memory releasing problem
Asked Answered
R

3

13

I'm a newby in C#. I have to repeatedly refresh a GUI picture box in a worker thread. The image is acquired from a camera polling a driver with a GetImage method that retrives the image to be displayed. Even if I allocate the bitmap using directive "using" and explicitly call G.C, memory seems to be never deallocated.

The worker thread is something like this:

   while (true)
    {
        // request image with IR signal values (array of UInt16)
        image = axLVCam.GetImage(0);
        lut = axLVCam.GetLUT(1);
        DrawPicture(image, lut);
        //GC.Collect();

    }

While the DrawPicture method is something like

   public void DrawPicture(object image, object lut)
{

  [...]

    // We have an image - cast it to proper type
    System.UInt16[,] im = image as System.UInt16[,];
    float[] lutTempConversion = lut as float[];

    int lngWidthIrImage = im.GetLength(0);
    int lngHeightIrImage = im.GetLength(1);

    using (Bitmap bmp = new Bitmap(lngWidthIrImage, lngHeightIrImage)) {

      [...many operation on bitmap pixel...]

        // Bitmap is ready - update image control

        //SetControlPropertyThreadSafe(tempTxtBox, "Text", string.Format("{0:0.#}", lutTempConversion[im[160, 100]]));

        //tempTxtBox.Text = string.Format("{0:00000}", im[160, 100]);
        //System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
        pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());
    }
}

Problems arises with the

pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());

In fact commenting that line of code, garbage collection works as it would. Better, the problem seems to be with

System.Drawing.Image.FromHbitmap(bmp.GetHbitmap())

Any advice to solve this memory leak?

Thanks a lot!

Rhythmics answered 2/12, 2009 at 9:29 Comment(0)
C
21

Image implements IDisposable, so you should call Dispose on each Image instance that you create, when it is no longer needed. You could try to replace this line in your code:

pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());

With this:

if (pic.Image != null)
{
    pic.Image.Dispose();
}
pic.Image = System.Drawing.Image.FromHbitmap(bmp.GetHbitmap());

This will dispose the previous image (if any) before the new one is assigned.

Cartier answered 2/12, 2009 at 9:33 Comment(6)
The problem is that he's not disposing a GDI object.Demineralize
@Yannick: good point. I imagine both issues create problems for the garbage collector.Disconnected
Indeed, by calling Dispose on Image you dispose unmanaged resources which get allocated when calling FromHbitmap(). However the current solution ignores the unmanaged GDI object created when calling GetHbitmap().Demineralize
Very good boys!! I had to delete/dispose both objects as you have indicated. DeleteObject(gdiBitmap); and pic.Image.Dispose() Now everythink work!!! You saved me! Thank a lot!Rhythmics
Just don't use GetHbitmap(), there's no point. Directly assign bmp to the Image property. Bitmap derives from Image.Bois
I was properly disposing of my Bitmap var, but I had overlooked a Graphics object. Dear readers: Don't forget to properly dispose (or use a using statement) for every object you use. They're easy to miss but if you do, your Diagnostics memory usage will just keep on rising. Lastly, when assigning the .Image property of your PictureBox, you may need to assign a .Clone() of the object (which will need to be casted).Wrathful
D
8

The thing is, you're making a GDI bitmap of bmp with GetHbitmap, which according to msdn:

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

Then the FromHbitmap method makes a copy of the GDI bitmap; so you can release the incoming GDI bitmap using the GDI DeleteObject method immediately after creating the new Image.

So basically I would add:

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

...

IntPtr gdiBitmap = bmp.GetHbitmap();

// Release the copied GDI bitmap
if (pic.Image != null)
{
    pic.Image.Dispose();
}

pic.Image = System.Drawing.Image.FromHbitmap(gdiBitmap);

// Release the current GDI bitmap
DeleteObject(gdiBitmap);

I am unsure if you need the GDI bitmap to perform some kind of transformation. In case you don't you can just assign the bitmap to the Image property of your PictureBox, and ignore the former solution:

// Since we're not using unmanaged resources anymore, explicitly disposing 
// the Image only results in more immediate garbage collection, there wouldn't 
// actually be a memory leak if you forget to dispose.
if (pic.Image != null)
{
    pic.Image.Dispose();
}

pic.Image = bmp;
Demineralize answered 2/12, 2009 at 9:40 Comment(0)
L
3

There are several ways to release an image from pbox. I strongly recommend the way is that do not use pbox.Image = Image.FromFile.... If you do not use FileStream and you want to read it from file use BitMap class. Like this:

Bitmap bmp = new Bitmap(fileName);
pbox.Image = bmp; // notice that we used bitmap class to initialize pbox.

... and then you want to release the image file bmp.Dispose();
Now you can delete, move or whatever you want to the file.

Lira answered 21/2, 2011 at 22:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.