.NET PictureBox - how to be sure that the resource is released - c#

I have an OpenFileDialog and PictureBox in user control. To understand the problem better I'll explain in few words how this user control works. The user can select an image to be opened for the form. The name of this image is saved in a DataBase and the file for the image is copied in a default location. When there is some image saved in the database it is load in the picturebox when the form with the picturebox control is loaded. If the user select another image and want to save the form with the new image I have a method that takes care to delete the old image file from my default location and that is where the problem occurs.
When I have loaded image and try to save new one, sometimes (very rare in fact) I get an error that The resource is being used by another process.. I can paste the exact error if needed. I think that the problem is caused from the picturebox and the way it deals with images.
Here is my code:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
if (MyImage != null)
{
MyImage.Dispose();
}
selectedFile = openFileDialog1.FileName;
selectedFileName = openFileDialog1.SafeFileName;
MyImage = new Bitmap(openFileDialog1.FileName);
pictureBox1.Image = (Image)MyImage;
int imageWidth = pictureBox1.Image.Width;
int picBoxWidth = pictureBox1.Width;
if (imageWidth != 0 && picBoxWidth > imageWidth)
{
pictureBox1.Width = imageWidth;
}
else
{
pictureBox1.Width = defaultPicBoxWidth;
}
}
catch (Exception ex)
{
logger.Error(ex.ToString());
MessageBox.Show("Error loading image!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
and my delete method:
public void DeleteImage(AppConfig imageInfo, string imageName)
{
string imgPath = imageInfo.ConfigValue.ToString();
try
{
File.Delete(imgPath + "\\" + imageName);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I thought that :
if (MyImage != null)
{
MyImage.Dispose();
}
will deal with this problem but still sometimes it occurs. And because it's not everytime it's even more critical to deal with it because at some point I may decide that I have solved it but in fact to be just lucky for some time.

MyImage = new Bitmap(openFileDialog1.FileName);
pictureBox1.Image = (Image)MyImage;
Yes, that code puts a lock on the file. The lock is produced by a memory mapped file object that GDI+ creates to efficiently map the pixel data of the file into memory without having to allocate space in the paging file. You will not be able to delete the file as long as the image is displayed in the picture box and not disposed, the lock prevents that. You will have to dispose the image and set the Image property back to null before you can delete the file.
You can prevent the file from getting locked by making an in-memory copy of the image:
using (var temp = new Bitmap(openFileDialog1.FileName)) {
pictureBox1.Image = new Bitmap(temp);
}
It is not as efficient of course, to be avoided if the image is large. And do beware that another process may in fact have a similar lock on the file. Nothing you can do about that.

A major difficulty with things like PictureBox is that because a PictureBox has no way of knowing whether it is the only user of an image, it consequently has no way of knowing whether it should dispose of that image when it no longer needs it.
Consequently, whatever code owns a picture box must also take ownership of the image associated therewith. There are three approaches I could suggest for doing so:
Create a control derived from PictureBox which documents itself as assuming ownership of any image given to it. Such a control should probably replace the image property with a SetImageWithOwnership method (with the semantics that once an image is passed to the PictureOwningBox, the box will be expected to "own" it, and will dispose it either when the box is Disposed or when a different image is given to the box).
Attach event handlers to a PictureBox to handle the scenarios where either the box is destroyed or a different image is assigned to it.
Have any code which would cause the PictureBox to be disposed or have a different image loaded, also dispose the Image that had been assigned to it.
While there may be cases where it would be appropriate to call GC.Collect and let the garbage-collector take care of things, such an approach is generally unsound.

try that:
using(Bitmap MyImage = new Bitmap(openFileDialog1.FileName))
{
pictureBox1.Image = (Image)MyImage;
int imageWidth = pictureBox1.Image.Width;
int picBoxWidth = pictureBox1.Width;
if (imageWidth != 0 && picBoxWidth > imageWidth)
{
pictureBox1.Width = imageWidth;
}
else
{
pictureBox1.Width = defaultPicBoxWidth;
}
}

I've had problems like this before, and one way that I've found to make sure that the resource is released, even after Dispose(), which really only marks the object for removal by the garbage collector, is by using GC.Collect(). I'm sure that there is a cleaner way to handle the resource disposal, but the time that it takes the GC.Collect() to run shouldn't hinder your program.

Related

How do I cache an image

Every time the user presses a button my application gets the image from the web, but this slow since the image isn't getting cached. Any way I can cache the image so it doesn't download it over and over making things faster.
This is my code so you know how my application works.
if (listBox1.SelectedIndex == 0)
{
richTextBox1.Text = "Explore any game with Dex";
pictureBox1.Load("THE LINK ");
ProtoxRe.lol = 0;
}
else if ((listBox1.SelectedIndex == 1))
{
richTextBox1.Text = "SOME TEXT";
pictureBox1.Load("LINK");
ProtoxRe.lol = 1;
}
There's probably more sophisticated methods, and this assumes your image is not expected to change, but you can do something like this:
//Image class is from this library
using System.Drawing;
//The scope of this will depend on where you need to access it from
Image img;
private Image GetImage()
{
if (img == null)
{
img = GetImageFromHttp();
}
return img;
}
This is very basic, and has a number of flaws; you're not caching it between sessions so every application start will require the http get, and you're not checking if the image on the web is different, but it's a starting point.

Image resource release in c# application

I am doing an image processing project using Windows Forms (c#). You can see the design of my application below.
What does this app do : take the original image, create a copy and modify the copy.
My app is working well but, if I process the same original image another time without closing the app, I get an error due to (I think) the display of the modified image. I think that the display on the bottom right corner uses the resources of the image and, when I try to modify it again, the system considers that the image is already used by another program so it can't be modified.
So my question is : "How can I stop using the modified image if the user clicks on PROCESS again ?"
I tried to use the .Dispose() method but it didn't work.
Code of the c# function linked to the PROCESS button :
private async void button8_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
if (csI != csP)
{
MessageBox.Show("The selected profil does not match the selected image. Colorspaces are different.", "WARNING",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
pictureBox2.Image = null;
if (checkBox2.Checked == false)
{
rendered = false;
button8.Enabled = false;
await Task.Run(() => wrapper.DominantColors(trackBar1.Value, rendered));
//wrapper.DominantColors(trackBar1.Value, rendered);
}
else
{
rendering = comboBox1.Text;
string outputImage = wrapper.Link(rendering, bpc);
rendered = true;
button8.Enabled = false;
await Task.Run(() => wrapper.DominantColors(trackBar1.Value, rendered));
//wrapper.DominantColors(trackBar1.Value, rendered);
}
// re-enable things
button8.Enabled = true;
progressBar1.Visible = false;
MessageBox.Show("processing done");
Bitmap bit = new Bitmap(imgDstPath);
float WidthImg = bit.Width;
float HeightImg = bit.Height;
float alpha = WidthImg / pictureBox2.Width;
float beta = HeightImg / pictureBox2.Height;
alpha = Math.Max(alpha, beta);
float newWidthf = WidthImg / alpha;
float newHeightf = HeightImg / alpha;
int newHeight = (int)newHeightf;
int newWidth = (int)newWidthf;
pictureBox2.ClientSize = new Size(newWidth, newHeight);
pictureBox2.Image = bit;
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
If possible, I'd like to clear the use of the resources when I click on the process button.
Thank you in advance for your help
The basic rule is that all objects you create that implements IDisposable need to be disposed. When writing winforms apps all controls added to a forms are disposed when the form is disposed. But whenever you change things you might need to handle disposal yourself.
For example:
pictureBox2.Image = bit;
If pictureBox2.Image is already set to something you need to ensure that it is disposed.
var oldImage = pictureBox2.Image;
pictureBox2.Image = bit;
oldImage.Dispose();
I'm not sure this is the actual problem you are having, your example code is insufficient to make that determination. To discover this you need to debug your program! Start by examining your exceptions, does it fail when opening a file or some other resource? Where was that resource created? where is it disposed? Perhaps even use a memory debugger to produce a list of all objects that are alive to see if there are any suspicious objects kept around. Will disposal correctly occur if any arbitrary code throws an exception?
It is sometimes useful to check the identity of objects in the debugger, to see if it has been switched out, or see what object your breakpoint was triggered in. You can rightclick an object in the watch panel in visual studio and select "Make ObjectId", this will associate a number with the object that appears at the end of the value.
If anyone in the future wants to know the solution I found, here it is :
At the beginning of the PROCESS function I added those simple lines :
if (pictureBox2.Image != null)
{
pictureBox2.Image.Dispose();
pictureBox2.Image = null;}

PictureBox assigned ErrorImage is not shown after incorrectly Disposing its Image

I have a winforms application where user can select an image from a list of available images and the corresponding image is shown in a PictureBox. The images can be very huge with a minimum of 10MB. This obviously makes the rest of the UI unresponsive while the image loads. So I thought of loading the image on a separate thread using the following code:
private void LoadImage()
{
// loadViewerThread is a Thread object
if (loadViewerThread != null && loadViewerThread.IsAlive)
{
loadViewerThread.Abort(); // Aborting the previous thread if the user has selected another image
}
loadViewerThread = new Thread(SetViewerImage);
loadViewerThread.Start();
}
The SetViewerImage function is as below:
private void SetViewerImage()
{
if (pictureBox1.Image != null)
pictureBox1.Image.Dispose();
pictureBox1.Image = new Bitmap(/*Some stream*/);
}
After this the images load smoothly and the UI can also be accessed.
But if the user moves very fast between the set of images then a big red X mark comes up. This happens because I have that call to Dispose in SetViewerImage.
I have assigned an ErrorImage to the PictureBox but that ErrorImage is never shown in this case.
Questions:
Is there anything wrong with my thread implementation? Why does the
Image gets disposed?
Is there any way that I can display a different ErrorImage and not
the red X?
You need to manipulate controls in the UI thread. You can do this by using Control.Invoke() from a different thread.
The big bottleneck is creating the image from the stream, so you should be able to reorganize your method like this to keep the UI thread freed up:
private void SetViewerImage()
{
Bitmap image = new Bitmap(/* Some stream*/);
pictureBox1.Invoke(new Action(() =>
{
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
pictureBox1.Image = null; // This might help? Only add if it does.
}
pictureBox1.Image = image;
}));
}

ArgumentException on Image.Dispose()

I am making an album application with windows forms and I have a problem that I can't solve. First of all I have a form where I create a TableLayoutPanel. After that I create a method where I generate the same amount of picture boxes as the amount of the images in the directory which I have opened. The problem occurs when I am trying to dispose the image which I load in the picturebox because I need to free its memory. Here is the code of the method:
public void createPictureBoxes()
{
Image loadedImage;
int imageCounter = 0;
for (int i = 0; i < rowCounter; i++)
for(int p = 0; p < imagesTable.ColumnCount; p++)
{
PictureBox pb = new PictureBox();
pb.SizeMode = PictureBoxSizeMode.Zoom;
pb.Width = imagesTable.GetColumnWidths()[p];
pb.Height = imagesTable.GetRowHeights()[i];
pb.Click += new EventHandler(enlargeThumbnail);
try
{
loadedImage = Image.FromFile(images[imageCounter++]);
pb.Image = loadedImage;
loadedImage.Dispose();
imagesTable.Controls.Add(pb);
loadedImage.Dispose();
}
catch (IndexOutOfRangeException)
{
break;
}
}
}
The program throws an ArgumentException on method Show() of the form telling me that the argument is not valid. Without the dispose method all works fine but if i try to load a large amount of images the program uses gigabytes of memory. I suppose that it is not right to dispose the image memory that way, but I can't come out with another idea. If someone could help I would be very grateful
Two problems: you're disposing twice, and further you won't be able to dispose of an image control so long as a parent control container is expected to use it. When the form is disposed then it will cause all controls to be disposed that are part of its container.
So, much less than trying to dispose twice, don't dispose at all (here, that is)!
You can't dispose the image while you are displaying it. If you do, the form can't display it.
The PictureBox doesn't make a copy of the Image instance when you assign it to the Image property. It keeps the instance, so you can't dispose it until you have removed the image from the picture box.

Why does my form throw an OutOfMemory exception while trying to load image?

I have an application that saves User Information with Image into a data base. Admin can access the information those are already saved through a different form view. Clicking on the List Box item will display the details with Image retrieved from the database.
UserViewDetails.cs:
private void lbEmp_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
if (lbEmp.SelectedIndex != -1)
{
em.Emp_ID = Convert.ToInt32(lbEmp.SelectedValue);
em.SelectById();
if (!em.EmptyPhoto)
pbEmp.BackgroundImage = em.Picture;
else
pbEmp.BackgroundImage = null;
txtEmpName.Text = em.Emp_Name;
txtImagePath.Text = em.ImgPath;
cmbEmpType.SelectedText = em.EmployeeType;
cmbCountry.SelectedValue = em.CountryID;
cmbCity.SelectedValue = em.CityID;
}
}
catch (Exception) { }
}
This form is called from the parent form Form1:
Form1.cs:
try
{
var vi = new Admin.frmViewEmployeeInfo();
vi.ShowDialog();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Here, an "out of memory" exception is caught. What is happening? The same code doesn't throw any exception in another application of mine.
An OutOfMemoryException is pretty common when you use the Bitmap class. Bitmaps can require a lot of memory. One standard way to get in trouble is being sloppy about calling its Dispose() method. Not using Dispose() in your code is something you'll get away easily in .NET, finalizers will clean up after you. But that tends to not work well with bitmaps because they take a lot of unmanaged memory to store the pixel data but very little managed memory.
There is at least one Dispose() call missing in your code, you are not disposing the old background image. Fix:
em.SelectById();
if (pbEmp.BackgroundImage != null) pbEmp.BackgroundImage.Dispose(); // <== here
if (!em.EmptyPhoto)
pbEmp.BackgroundImage = em.Picture;
else
pbEmp.BackgroundImage = null;
And possibly in other places, we can't see how em.Picture is managed.
Also, and much harder to diagnose, is that GDI+ is pretty poor at raising accurate exceptions. You can also get OOM from a file with bad image data. You'll find a reason for that regrettable behavior in this answer.

Categories

Resources