Loading an image from a stream without keeping the stream open - c#

Is it possible to use the FromStream method of System.Drawing.Image without having to keep the stream open for the lifetime of the image?
I have an application which loads a bunch of toolbar graphics from resource files, using a combination of Image.FromStream and Assembly.GetManifestResourceStream.
The problem I'm having is while this works fine on Windows 7, on Windows XP the application crashes if a user interface element linked to one of these images is disabled. On Windows 7, the image is rendered in grayscale. On XP, it crashes with an out of memory exception.
After a load of hairpulling I have finally traced it to the initial loading of the image. As a matter of course, if I create any object implementing IDisposable that is also destroyed in the same method, I wrap it in a using statement, for example
using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
{
image = Image.FromStream(resourceStream);
}
If I remove the using statement so that the stream isn't disposed, then the application no longer crashes on XP. But I now have a bunch of "orphan" streams hanging about - the images are stored in command classes and these correctly dispose of the images when they themselves are disposed, but the original stream isn't.
I checked the documentation for FromStream and it confirms the stream needs to remain open. Why this hasn't crashed and burned on the Windows 7 development system is a mystery however!
I really don't want this stream hanging around, and I certainly don't want to have to store a reference to this stream as well as the image so I can dispose of it later. I only have need of that stream once so I want to get rid of it :)
Is it possible to create the image and then kill of the stream there and then?

The reason the stream needs to be open is the following:
GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object.
The documented workaround is to create either a non-indexed image using Graphics.DrawImage or to create an indexed Bitmap from the original image as described here:
Bitmap and Image constructor dependencies

According to the documentation of Image.FromStream, the stream must be kept open while the image is in use. Therefore, even if closing worked (and there's nothing to say you can't close a stream before it's disposed, as far as the stream object itself goes) it may not be a very reliable approach.
You could copy the image to another image object, and use that. However, this is likely to be more memory intensive than just keeping the stream open.

You could save the stream to a temporary file and use the Image.FromFile method. Or simply don't embed the image, keep it as a file and load it from this file at runtime.

I'am sure this will help someone :)
I used it for my dataGridView_SelectionChanged:
private void dataGridViewAnzeige_SelectionChanged(object sender, EventArgs e)
{
var imageAsByteArray = File.ReadAllBytes(path);
pictureBox1.Image = byteArrayToImage(imageAsByteArray);
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}

Related

How i can free my application memory in c#?

in my c# application load form i make load like 30 picture and this make my memory full and if I add more pictures I'll get the message "that my memory is full"
how can I free my memory or increase the maximum used memory?
is there are any another way to get all my pictures to picture box without filling my memory?
1) how can I free my memory or increase the maximum used memory?
Are you receiving an OutOfMemoryException and your memory isn't filled up in your machine? This might be due to x86 compilation and you should change it to x64.
2) is there are any another way to get all my pictures to picture box without filling my memory?
Yes and no. If you are using Bitmap it will allocate the image in the memory. If you are recreating the image later with all the images together without compressing it, then you could be also losing some memory if you are not disposing the Bitmaps correctly. You could load them, compress them, and then dispose the original one, keeping the compressed/optimized one in memory.
Besides, remember to always dispose your Bitmap after using it. The GC usually takes care of them, but you should always dispose them:
using (Bitmap bitmap = new Bitmap("file.jpg")
{
// bitmap handling here
}
Of course, if you are displaying it in a picturebox you can't dispose it, but again, you are not being completely clear in your question.
Without your code I cannot provide you a better answer, and I'll gladly edit this one if you update your question.
Read:
Compress bitmap before sending over network
When do I need to use dispose() on graphics?
Update
Upon fixing your thread I've noticed you've given an image but the formatting was broken.
Read my answer and it should help you (specially the part of compression). You can also change to x64.
Update 2 - Compression code example
public void ExampleMethod()
{
pictureBox1.Image = GetCompressedFile("file.jpg", quality: 10);
}
private Image GetCompressedFile(string fileName, long quality)
{
using (Bitmap bitmap = new Bitmap(fileName))
{
return GetCompressedBitmap(bitmap, quality);
}
}
private Image GetCompressedBitmap(Bitmap bmp, long quality)
{
using (var mss = new MemoryStream())
{
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
ImageCodecInfo imageCodec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(o => o.FormatID == ImageFormat.Jpeg.Guid);
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = qualityParam;
bmp.Save(mss, imageCodec, parameters);
return Image.FromStream(mss);
}
}
In the quality parameter you set the quality. 100 means 100%, 50 means 50%.
Try setting quality as 10 and see if it works.
Remove all Bitmap bmp = new Bitmap from the code, and display with your PictureBoxInstance.Image = GetCompressedFile(...);
Check the ExampleMethod().
Code based from: https://stackoverflow.com/a/48274706/4352946
You have to remember though, that even using Dispose, the memory won’t be freed right away, you should wait the GC for that.
P.S: compressing the file on-the-go you MIGHT end up with both images (the compressed and uncompressed) in the memory

Large Amounts of Byte[] Kept in Memory by StreamAsIStream Instances

I have been memory-profiling a C# .NET WPF application, and I have found that there is a large number of Byte[] instances staying in memory. They are predominantly kept alive by instances of StreamAsIStream handles, which reference System.IO.MemoryStream instances, which finally reference the System.Byte[] instances.
These System.Byte[] instances amount to over 5mb of memory, so I'd like to understand why they're sitting in memory and if there's anything I can do to get rid of them (unless they're necessary, of course).
I've tried to do some research on my own, but I have been unable to find much. The only thing I've seen pertains to the use of ElementHost when hosting WinForms elements in a WPF application, but ElementHost is not used throughout the program.
I was able to put breakpoints in the constructor of SystemAsIStream and found that these objects are instantiated when I create new instances of BitmapImage using streams, but they appear to persist even after the BitmapImage instances have been GC'd.
I have also used WinDBG to try to examine the SystemAsIStream instances, but I am not very experienced with using that program. The most I could gather was that the instances are strong handles, but I basically knew that already.
Does anybody have any idea why this might be happening and what I can do about it? Even more information about StreamAsIStream would be appreciated.
Further information: I'm using .NET 4.0 and my target platform is x86.
Edit: the BitmapImage instances are created with the following code:
public static BitmapImage BitmapImageFrom(Stream stream)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.None;
image.StreamSource = stream;
image.EndInit();
return image;
}
The stream is from the following:
stream = Application.GetResourceStream(new Uri(string.Format(#"pack://application:,,,/Images/{0}/{1}", folder, imageName))).Stream;
I've tried creating the BitmapImages by setting their CacheOption field to BitmapCacheOption.OnLoad and disposing of the stream, but that doesn't seem to make a difference (could be wrong -- I may have done it wrong or something).

A Generic error occured in GDI+ in Image.Save() method

I'm trying to use this class but I'm getting a Generic error occured in GDI+ in Image.Save() method. From what I read it's some stream I need to close but I don't know which one.
I'm calling using this:
Image image = Image.FromFile(#"C:\a.jpg");
using (var resized = ImageUtilities.ResizeImage(image, 50, 100))
{
//save the resized image as a jpeg with a quality of 90
ImageUtilities.SaveJpeg(#"C:\myimage.jpeg", resized, 90);
}
Why is that error and how do I solve this?
Unless your program is running as administrator you can not save directly to the root of C: make a folder and save it inside there instead.
Have you tested saving the images in different locations?
If it is still failing then without knowing exactly what is going on in your code I would hazard a guess to say that the original image is getting disposed somewhere before it should be. That's usually the most common cause of the error.
I've written a library that handles many different imaging operations whilst ensuring that memory is correctly handled. It's well tested and very simple to use.
You can get it here. http://imageprocessor.org/
Example code using the library:
using (ImageFactory imageFactory = new ImageFactory())
{
// Load, resize, set the quality and save an image.
imageFactory.Load(#"C:\a.jpg")
.Resize(new Size(50, 100))
.Quality(90)
.Save(#"C:\myimage.jpeg);
}

The dispose of an OpenFileDialog in C#?

I have searched throughout entire Stack Overflow, but I couldn't find an answer to the following:
When I'm using my OpenFileDialog, the files I open get blocked for use out of my program until I close my program. So if I open an image, I am not allowed to replace that image in my Windows Explorer anymore.
I think this is a problem with disposing my OpenFileDialog, but I'm not sure how to solve it...
My code:
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Title = "Open Image";
ofd.Filter = "PNG Image(*.png|*.png" +
"|GIF Image(*.gif|*.gif" +
"|Bitmap Image(*.bmp|*.bmp" +
"|JPEG Compressed Image (*.jpg|*.jpg";
if (ofd.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(ofd.FileName);
}
}
I thought that the using block would solve this problem, but nope... It still gets used by the program. I want to load the image in the picturebox and then I want the image to be available again (so I can rename it, replace it, etc...).
This is not related to the OpenFileDialog. It couldn't possibly be, because the dialog doesn't actually open the file. It just allows the user to select a file to open, and then returns that path to you so that you can write code that opens the file. Besides, you're correctly disposing of the OpenFileDialog through your use of the using statement.
The problem here comes from your actually opening the file—ofd.FileName—as a Bitmap. When you use the Bitmap constructor overload that accepts a path string, the file on disk that contains the image remains locked until the Bitmap object is disposed. So says the documentation:
The file remains locked until the Bitmap is disposed.
Because you're assigning the bitmap to pictureBox1.Image, the Bitmap object will not be disposed until pictureBox1 is disposed. And therefore your image file on disk will remain locked.
If you want to unlock the file, you will either need to make a copy of the bitmap and dispose the original, or clear out the PictureBox and dispose its previous image when you are finished with it.
As I understand your question, it sounds like you want to be able to make changes to the image file on disk while continuing to display the image in the picture box. If that's the case, you will need to make a copy. Do that using the constructor overload that takes an Image, like this:
if (ofd.ShowDialog() == DialogResult.OK)
{
// Load the image the user selected
using (Image img = Image.FromFile(ofd.FileName))
{
// Create a copy of it
Bitmap bmpCopy = new Bitmap(img);
// Clear out the bitmap currently in the picture box,
// if there is one.
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
// Assign the copy of the bitmap to the picture box.
pictureBox1.Image = bmpCopy;
}
}
As written by Chris, try something like:
pictureBox1.Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(old.FileName)));
It reads all the file with File.ReadAllBytes, put it in a MemoryStream and pass the MemoryStream to the Image static initializer.
Equivalent to:
byte[] bytes = File.ReadAllBytes(old.FileName);
MemoryStream ms = new MemoryStream(bytes);
pictureBox1.Image = Image.FromStream(ms);
You mustn't dispose the MemoryStream! If/when the Image will be disposed, the finalizer of MemoryStream will kick in (if you don't have other references to ms) and the MemoryStream will be disposed (note that this isn't something that will happen immediately... It's something that will happen when the GC will run)
The technique I've found to be best is to read the file into a byte array with File.ReadAllBytes() (that opens and closes the file), and then use ImageConverter to convert the byte array into an Image. See here for example: https://stackoverflow.com/a/16576471/253938
Edit:
Quote from that previous post of mine: "Some of the other techniques I've tried have been non-optimal because they changed the bit depth of the pixels (24-bit vs. 32-bit) or ignored the image's resolution (dpi)."

When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI

I've got a global Graphics object created from a Panel. At regular intervals an image is picked up from the disk and drawn into the panel using Graphics.DrawImage(). It works fine for a few iterations and then I'm getting the following helpful exception:
System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y)
at System.Drawing.Graphics.DrawImage(Image image, Point point)
I ruled out memory leaks as I dispose of the image object when I'm done with it. I know that the images are not corrupted and can be read fine as the program executes fine for a while before the panel stops showing.
I ran into the same problem when using a PictureBox but this time at least I got an error instead of nothing.
I checked the GDI objects and USER objects in the Task Manager but they're always around 65 user objects and 165 GDI objects when the app works and when it doesn't.
I do need to get to the bottom of this as soon as and it's not like I can stick breakpoints in .NET System libraries and see where exactly execution fails.
Thanks in advance.
EDIT: This is the display code:
private void DrawImage(Image image)
{
Point leftCorner = new Point((this.Bounds.Width / 2) - (image.Width / 2), (this.Bounds.Height / 2) - (image.Height / 2));
_graphics.DrawImage(image, leftCorner);
}
the image load code:
private void LoadImage(string filename, ref Image image)
{
MemoryStream memoryStream = DecryptImageBinary(Settings.Default.ImagePath + filename, _cryptPassword);
image = Image.FromStream(memoryStream);
memoryStream.Close();
memoryStream.Dispose();
memoryStream = null;
}
_image is global and its reference is updated in LoadImage. They are passed as parameters as I want to change the global references from as few places as possible only and keep the other methods self contained. _graphics is also global.
I've also got a webBrowser control for web sites and I either show an image or a website at one time. when there's time to display an image, the following code executes:
webBrowser.Visible = false;
panel.Visible = true;
DrawImage(_image)
_image.Dispose();
_image = null;
_image is referencing a pre-loaded image.
Hope this helps.
Your problem is similar to what I thought, but not quite. When you are loading the image, you are loading it from a MemoryStream. You have to keep the stream open for the lifetime of the image, see MSDN Image.FromStream.
You must keep the stream open for the lifetime of the Image.
The solution is to make a copy of your image in the FromImage function:
private void LoadImage(string filename, ref Image image)
{
using (MemoryStream memoryStream = DecryptImageBinary(Settings.Default.ImagePath + filename, _cryptPassword))
{
using (tmpImage = Image.FromStream(memoryStream))
{
image = new Bitmap(tmpImage);
}
}
}
Similar to the dispose problem I mentioned, the image will seem to work and then randomly fail when the underlying stream is garbage collected.
Without a little more code there is not enough to properly diagnose here, however, one thing to look at is that you may have disposed on the image your a drawing with at some point earlier and it is only after the garbage collector runs that your code is failing. Are you using cloned images anywhere? One thing I was suprised to learn is that if you do a straight clone of an image, you are not cloning the underlying bitmap that the image rely's upon, only the image structure, to create a proper copy of an image you have to create a new image:
var newImage = new Bitmap(img)
as
var newImage = oldImg.Clone();
oldImg.Dispose();
...
gr.DrawImage(newImage, new Rectangle(0,0,newImage.Width,newImage.Height);
will work for a while, but then fail at some random point...

Categories