PictureBox Not Releasing Resources - c#

My application is used to store scanned images, with additional information, to an SQL Database. Because I use a picturebox to accomplish this there is an issue I've become aware of with the picturebox holding open resources. This is preventing me from doing anything with the original file until I close my form. I have tried various ways to dispose of the picturebox with no success. Need help with the following code to release the resources held by the picturebox.
using (OpenFileDialog GetPhoto = new OpenFileDialog())
{
GetPhoto.Filter = "images | *.jpg";
if (GetPhoto.ShowDialog() == DialogResult.OK)
{
pbPhoto.Image = Image.FromFile(GetPhoto.FileName);
txtPath.Text = GetPhoto.FileName;
txtTitle.Text = System.IO.Path.GetFileNameWithoutExtension(GetPhoto.Fi‌​leName);
((MainPage)MdiParent).tsStatus.Text = txtPath.Text;
//GetPhoto.Dispose(); Tried this
//GetPhoto.Reset(); Tried this
//GC.Collect(): Tried this
}
}
Saving the image to my database uses the following:
MemoryStream stream = new MemoryStream();
pbPhoto.Image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] pic = stream.ToArray();

It's usually FromFile() that causes locking issues: (not the PictureBox itself)
The file remains locked until the Image is disposed.
Change:
pbPhoto.Image = Image.FromFile(GetPhoto.FileName);
To:
using (FileStream fs = new FileStream(GetPhoto.FileName, FileMode.Open))
{
if (pbPhoto.Image != null)
{
Image tmp = pbPhoto.Image;
pbPhoto.Image = null;
tmp.Dispose();
}
using (Image img = Image.FromStream(fs))
{
Bitmap bmp = new Bitmap(img);
pbPhoto.Image = bmp;
}
}
This should make a copy of the image for use in the PictureBox and release the file itself from any locks.

Related

Image to byte array fails "A generic error occurred in GDI+."

let's assume I let the user choose an image from the computer. I load the file to a picture box. here is the conversion method:
public static Image LoadImageFromFile(string fileName)
{
Image result = null;
if (!File.Exists(fileName))
return result;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
try
{
using (Image image = Image.FromStream(fs))
{
ImageManipulation.RotateImageByExifOrientationData(image);
result =(Image) image.Clone();
image.Dispose();
}
}
finally
{
fs.Close();
}
return result;
}
then, when the user clicks on the Save button, I convert the image into a byte array and save it into the database. here is the conversion code:
public static byte[] ImageToByteArray(Image image)
{
if (image == null)
return null;
using (var ms = new MemoryStream())
{
ImageFormat imageFormat = image.RawFormat;
ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.FormatID == imageFormat.Guid);
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
if (codec != null)
image.Save(ms, codec, encoderParameters);
else
image.Save(ms, ImageFormat.Jpeg);
return ms.ToArray();
}
}
but the problem:
I have a jpg file on the disk. I can load it into my picture box without any problem. the picture is perfectly visible in it. but when I save it, the code gives me "A generic error occurred in GDI+." Error at 'image.Save(ms, codec,encoderParameters)' line.
more odd incident: I don't get this error all the time. it is happening with specific images. for example, I downloaded an image from the internet and crop it in "Paint" and saved it as jpg. error happened. open it in Paint again and save it as png. no error!!!! that is why I am really confused . and Yes I already have tried to don't dispose the image. not helping
I know it might be a stupid question but I am desperately stuck here :)
Thank you in Advanced
I didn't find out why my code is not working but I just substitute my loading method with the code below. it is releasing the file and at the same time works as it should.
public static Image LoadImageFromFile(string fileName)
{
using (Bitmap bmb = new Bitmap(fileName))
{
MemoryStream m = new MemoryStream();
bmb.Save(m, ImageFormat.Bmp);
return Image.FromStream(m);
}
}

PDFSharp Locking Tiff Files

I'm inserting a TIFF file into a PDF using PDFSharp. That process works fine, but it's leaving a lock on the TIFF file. The TIFF file is on a SMB share. I am using the WPF version because the GDI version does not support CMYK TIFFs.
var output = new PdfDocument();
var input = PdfReader.Open(template_path, PdfDocumentOpenMode.Import);
var page = input.Pages[0];
output.AddPage(page);
page = output.Pages[0];
var gfx = XGraphics.FromPdfPage(page);
var image = XImage.FromFile(tiff_path);
gfx.DrawImage(image, 500, 200, 400, 400);
output.Save(destination_path);
output.Close();
Update: Simply doing this leaves the TIFF locked. No document opened or XGraphics or anything.
using (var image = XImage.FromFile(path))
{}
Update: This works, and is what I am going with for now.
using (var fsImage = File.Open(tiffPath, FileMode.Open, FileAccess.Read, FileShare.None))
{
var bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.StreamSource = fsImage;
bitmapSource.EndInit();
using (var image = XImage.FromBitmapSource(bitmapSource))
{
}
}
Indecently, this nasty piece of code works also :-)
using (var image = XImage.FromFile(tiffPath))
{
}
GC.Collect();
With WPF BitmapSource, there is no deterministic disposal of the underlying stream, so you can end up with locks for as long as there is a reference.
You --> XImage --> BitmapSource --> Stream
If you call dispose on the XImage, it will release its reference on the BitmapSource, which will allow it to be finalized when the GC feels like it.
You can control when the file is closed by providing stream in lieu of a path and closing it explicitly. Doing so prematurely will cause exceptions in BitmapSource, however, so be sure you are not using the BitmapSource after you close the stream.
using (var fsImage = File.Open(tiff_path, FileMode.Open, FileAccess.Read, FileShare.None))
{
var output = new PdfDocument();
var input = PdfReader.Open(template_path, PdfDocumentOpenMode.Import);
var page = input.Pages[0];
output.AddPage(page);
page = output.Pages[0];
var gfx = XGraphics.FromPdfPage(page);
var bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.StreamSource = fsImage;
bitmapSource.EndInit();
using (var image = XImage.FromBitmapSource(bitmapSource))
{
gfx.DrawImage(image, 500, 200, 400, 400);
}
output.Save(destination_path);
output.Close();
}
If your image is small enough, you could skip the stream and just use the BitmapCacheOption of OnLoad to close the source after opening, but this will cause the entire image to be loaded into memory.

Unlocking image from PictureBox

I'm currently developing an application to help scanning and showing images here at my work.
My application is build with multiple forms, the most important forms here is my mainForm to show statistics about the current scanning and a menustrip with different functinalities. I also have ImageViewerForm with a PictureBox which shows on the secondary monitor to view the current scanned image.
I'm using a Timer to poll the folder where the images are scanned to. When a new image has been scanned and the image is unlocked, i'll grap it into a FileStream and show it in the PictureBox, see below:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
if (pb.InvokeRequired)
{
pb.Invoke(new MethodInvoker(
delegate()
{
pb.Image = currentImage;
}));
}
else
{
pb.Image = currentImage;
}
}
}
catch (Exception imageEx)
{
throw new ExceptionHandler("Error when showing image", imageEx);
}
}
public static Image ScaleImage(Image imgToResize, Size size)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap b = new Bitmap(destWidth, destHeight);
using (Graphics g = Graphics.FromImage(b))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
}
return b;
}
This way the image shown in the PictureBox shouldn't be locked, but it is.
The problem is that the scanned image might have to be rescanned, and if I do that i'll get a sharing violation error when trying to overwrite the image file from the scanning software.
Anyone who's got an answer to what I can do?
SOLUTION
Thanks to #SPFiredrake I've got a solution to create a temp file to show in the PictureBox, leaving the original image unlocked.
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(CreateTempFile(filename), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
if (pb.InvokeRequired)
{
pb.Invoke(new MethodInvoker(
delegate()
{
pb.Image = currentImage;
}));
}
else
{
pb.Image = currentImage;
}
}
}
catch (Exception imageEx)
{
throw new ExceptionHandler("Error when showing image", imageEx);
}
}
public static string CreateTempFile(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException("fileName");
if (!File.Exists(fileName))
throw new ArgumentException("Specified file must exist!", "fileName");
string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + Path.GetExtension(fileName));
File.Copy(fileName, tempFile);
Log.New("Temp file created: " + tempFile);
return tempFile;
}
The problem here is that the image is being loaded from a FileStream, which is being locked by the PictureBox because it's holding a reference to the stream. What you should do is first load the picture into local memory (via a byte[] array) and then load the image from a MemoryStream instead. In your SetPicture method, you should try the following change and see if it works:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
byte[] imageBytes = File.ReadAllBytes(filename);
using(MemoryStream msImage = new MemoryStream(imageBytes))
{
currentImage = ScaleImage(Image.FromStream(msImage), new Size(pb.Width, pb.Height));
....
}
Edit: After our conversation in Chat, updating with the fix that you ended up using:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
string tempFile = Path.Combine(Path.GetTempDirectory(), Guid.NewGuid().ToString() + Path.GetExtension(filename));
File.Copy(filename, tempFile);
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
...
This way you are using the temp files to actually load the picture box, leaving the original file untouched (outside of the initial copy).
Once a Bitmap is loaded, you're not holding onto the filestream anymore, so everything should work. However, if you're talking about the instant when loading is taking place and the scanning tries to overwrite that file - always scan to a "temporary" or junk-named file (use a GUID as the name). Once scanning is complete, rename that file to JPG - which your display form will then pick up and display properly.
This way, re-scans will only involve trying to rename the temporary file multiple times with a "wait" to prevent that little area of overlap.
Your code works fine for me. I took an exact copy and called it repeatedly with the same image file.
SetPicture(#"c:\temp\logo.png", pictureBox1);
Something else is locking the file. Can you share your calling code?
I guess you're done with your work by now.
Still, I am posting in case someone else has the same issue.
I had the same problem : I load an image in a PictureBox control
picture.Image = new Bitmap(imagePath);
and when attempting to move it
File.Move(source, destination);
mscorlib throws an exception :
The process cannot access the file because it is beeing used by another process
I found a solution ( although in VB.Net rather than C# ) here PictureBox "locks" file, cannot move/delete
The author of the post clones the original image and loads the cloned image to the PictureBox control.
I slighty changed the code and came up with this :
private Bitmap CloneImage(string aImagePath) {
// create original image
Image originalImage = new Bitmap(aImagePath);
// create an empty clone of the same size of original
Bitmap clone = new Bitmap(originalImage.Width, originalImage.Height);
// get the object representing clone's currently empty drawing surface
Graphics g = Graphics.FromImage(clone);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
// copy the original image onto this surface
g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height);
// free graphics and original image
g.Dispose();
originalImage.Dispose();
return clone;
}
So, we code will be :
picture.Image = (Image)CloneImage(imagePath);
Doing so, I had no more exception when moving files.
I think it is a good alternative way to do that, and you don't need a temporary file.
this is Jack code but in Visual Basic .NET and the casting goes inside the function
Private Function CloneImage(aImagePath As String) As Image
' create original image
Dim originalImage As Image = New Bitmap(aImagePath)
' create an empty clone of the same size of original
Dim clone As Bitmap = New Bitmap(originalImage.Width, originalImage.Height)
' get the object representing clone's currently empty drawing surface
Dim g As Graphics = Graphics.FromImage(clone)
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed
' copy the original image onto this surface
g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height)
' free graphics and original image
g.Dispose()
originalImage.Dispose()
Return CType(clone, Image)
End Function
So calling will be
picture.Image = CloneImage(imagePath)
Thank you Jack,
Response to this question from MS ...
for me is workikng ok ...
internal void UpdateLastImageDownloaded(string fullfilename)
{
this.BeginInvoke((MethodInvoker)delegate()
{
try
{
//pictureBoxImage.Image = Image.FromFile(fullfilename);
//Bitmap bmp = new Bitmap(fullfilename);
//pictureBoxImage.Image = bmp;
System.IO.FileStream fs;
// Specify a valid picture file path on your computer.
fs = new System.IO.FileStream(fullfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
pictureBoxImage.Image = System.Drawing.Image.FromStream(fs);
fs.Close();
}
catch (Exception exc)
{
Logging.Log.WriteException(exc);
}
});
}
While trying to figure out a solution for my C# Windows Form I came across a helpful article mentioning how to load a picture in picture box without "Locking" the original picture itself but an instance of it.
Hence if you try to delete, rename or do whatever you want to the original file you won't be notified by an error message mentioning "The file it's in use by another process" or anything else!
This is a reference to the article.
To Sum Up I believe that this solution is VERY useful when handling with SMALL amount of pictures, because applying this method in larger numbers could lead to lack of memory.

A generic error occurred in GDI+

I loaded an image into a Picture Box using:
picturebox1.Image = Image.FromFile()
and I save it by using:
Bitmap bm = new Bitmap(pictureBox1.Image);
bm.Save(FileName, ImageFormat.Bmp);
It works perfectly fine when creating a new file, but when I try to replace the existing image, I get thrown the following runtime error:
A generic error occurred in GDI+
So what can I do to solve this problem??
That because the image file is used by your picturebox1.Image, try to save it to different file path instead:
picturebox1.Image = Image.FromFile(FileName);
Bitmap bm = new Bitmap(pictureBox1.Image);
bm.Save(#"New File Name", ImageFormat.Bmp);
Edit: You could also add a copy from the image at the first place like:
picturebox1.Image = new Bitmap(Image.FromFile(FileName));
Bitmap bm = new Bitmap(pictureBox1.Image);
bm.Save(FileName, ImageFormat.Bmp);//no error will occurs here.
The FromFile method locks the file, so use the Image.FromStream() method for reading the image:
byte[] bytes = System.IO.File.ReadAllBytes(filename);
System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes);
pictureBox1.Image = Image.FromStream(ms);
Then save like you were before.
This can also happen if the path does not exist.
You could create the dir with:
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(FileName));
When either a Bitmap object or an Image object is constructed from a file, the file remains locked for the lifetime of the object. As a result, you cannot change an image and save it back to the same file where it originated. http://support.microsoft.com/?id=814675
A generic error occurred in GDI+, JPEG Image to MemoryStream:
Image.Save(..) // throws a GDI+ exception because the memory stream is closed
http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html
EDIT: Just writing from memory. Saving to an 'intermediary' MemoryStream should work:
For example, replace this:
Bitmap newBitmap = new Bitmap(thumbBMP);
thumbBMP.Dispose();
thumbBMP = null;
newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);
with something like:
string outputFileName = "...";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
thumbBMP.Save(memory, ImageFormat.Jpeg);
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
try this.
picturebox1.Image = Image.FromFile(FileName);
Bitmap bm = new Bitmat(pictureBox1.Image);
Image img = (Image)b;
img.Save(FileName, ImageFormat.Bmp);
Just like #Jalal Aldeen Saa'd said, the picture box is using the file and locked from file replacement.
//unlock file by clearing it from picture box
if (picturebox1.Image != null)
{
picturebox1.Image.Dispose();
picturebox1.Image = null;
}
//put back the picture inside the pictureBox?
try this it will work
public void SavePicture()
{
Bitmap bm = new Bitmap(this.myBitmap)
bm.Save("Output\\out.bmp" ,System.Drawing.Imaging.ImageFormat.Bmp );
}
This can also happen if you forget to add the filename:
bm.Save(#"C:\Temp\Download", System.Drawing.Imaging.ImageFormat.Png);
And can be fixed by adding the file name:
bm.Save(#"C:\Temp\Download\Image.png", System.Drawing.Imaging.ImageFormat.Png);
Note: You don't actually have to add the extension for it to work.
Try this:
private void LoadPictureBoxWithImage( string ImagePath)
{
Stream objInputImageStream = null;
BitmapData bmdImageData = null;
Bitmap bmpSrcImage = null, bmTemp = null;
byte[] arrImageBytes = null;
int bppModifier = 3;
try
{
objInputImageStream = new MemoryStream();
using (FileStream objFile = new FileStream(ImagePath, FileMode.Open, FileAccess.Read))
{
objFile.CopyTo(objInputImageStream);
}
bmpSrcImage = new Bitmap(objInputImageStream);
bppModifier = bmpSrcImage.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4;
//reda from byte[] to bitmap
bmdImageData = bmpSrcImage.LockBits(new Rectangle(0, 0, bmpSrcImage.Width, bmpSrcImage.Height), ImageLockMode.ReadOnly, bmpSrcImage.PixelFormat);
arrImageBytes = new byte[Math.Abs(bmdImageData.Stride) * bmpSrcImage.Height];
System.Runtime.InteropServices.Marshal.Copy(bmdImageData.Scan0, arrImageBytes, 0, arrImageBytes.Length);
bmpSrcImage.UnlockBits(bmdImageData);
pbSetup.Image = (Bitmap)bmpSrcImage.Clone();
pbSetup.Refresh();
}
catch (Exception ex)
{
throw new Exception("Error in Function " + System.Reflection.MethodInfo.GetCurrentMethod().Name + "; " + ex.Message);
}
finally
{
if (objInputImageStream != null)
{
objInputImageStream.Dispose();
objInputImageStream = null;
}
if (bmdImageData != null)
{
bmdImageData = null;
}
if (bmpSrcImage != null)
{
bmpSrcImage.Dispose();
bmpSrcImage = null;
}
if (bmTemp != null)
{
bmTemp.Dispose();
bmTemp = null;
}
if (arrImageBytes != null)
{
arrImageBytes = null;
}
}
}
A generic error occurred in GDI+
I also faced the same issue. I tried so many ways to fix this issue. Finally, I found a place where I have gone wrong. The problem is that I used space in the file path, which is not acceptable. Now it is working fine after removing the space in front of C after the apostrophe:
"SupplyItems":"C:\\inetpub\\HIBMS_Ver1\\BarcodeImages\\Supply\\"
instead... I used below one.
"SupplyItems":" C:\\inetpub\\HIBMS_Ver1\\BarcodeImages\\Supply\\"
Minor mistake but took a long time to find and to fix it.
Note that images created by Image.Clone() will still cause GDI+ errors as shown by the BAD code below, you must use the Image.FromStream() method for reading the image as shown in the solution on this page.
//BAD CODE: the image we will try to save AFTER the original image has been cloned and disposed
Image clonedImage;
//load image from file, clone it then dispose
using (var loadedFromDiskImage = Image.FromFile(filePath))
{
clonedImage = (Image) loadedFromDiskImage.Clone();
}
//you might think the new image can be saved given the original is disposed
//but this doesn't seem to be the way Clone() works
//expect GDI+ error in line below:
clonedImage.Save(filePath);

Black image when saving PNG from clipboard

I am trying to save a PNG image that has been copied to the clipboard, but it is either turning out as a solid black, or black around the areas that should be transparent.
Here is the code I am using to capture and save the Image
var clipboardImage = (InteropBitmap)Clipboard.GetImage();
Image.SaveImage(clipboardImage, Path.Combine(Config.App.ApplicationDataImagesPath, string.Format("{0}.{1}", imageId, "png")));
public static void SaveImage(BitmapSource bitmapImage, string filename)
{
using (var fileStream = new FileStream(filename, FileMode.Create))
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapImage));
pngBitmapEncoder.Save(fileStream);
fileStream.Close();
fileStream.Dispose();
}
}
Does anyone have any ideas why it won't persrve the alpha channels of a PNG?
Thanks
Dan
Edit: I should of mentioned that black images were happening when copying an image from Internet Explorer 9. Works perfectly when copying an image from either Chrome or Firefox. Any workarounds for IE9 issue?
What happens if just do this:
Clipboard.GetImage().Save ("XXX.png", System.Drawing.Imaging.ImageFormat.Png);
EDIT - for WPF try this:
public static void SaveClipboardImageToFile(string filePath)
{
var image = Clipboard.GetImage();
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(fileStream);
}
}

Categories