Memory leak in loading an image C# - wpf

I wrote a code to load the image into a <image> control and since i need to edit and save the same image which is used in multiple places, i was in a position to modify the code to avoid Access violation error. Now i'm getting Out of memory exception.
private BitmapSource LoadImage(string path)
{
lock (_syncRoot) //lock the object so it doesn't get executed more than once at a time.
{
BitmapDecoder decoder = null;
try
{
//If the image is not found in the folder, then show the image not found.
if (!File.Exists(path) && (path != null))
{
using (var stream = new System.IO.MemoryStream())
{
if (!File.Exists(Path.GetTempPath() + "ImageNotFound.jpg"))
{
System.Drawing.Bitmap ss = Ashley.ProductData.MarketSeries.Presentation.Properties.Resources.ImageNotFound;
using (FileStream file = new FileStream(Path.GetTempPath() + "ImageNotFound.jpg", FileMode.Create, FileAccess.Write))
{
ss.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
stream.WriteTo(file);
}
}
}
path = Path.Combine(Path.GetTempPath(), "ImageNotFound.jpg");
NoImage = false;
}
else
{
if (!EnableForEdit)
NoImage = false;
else
NoImage = true;
}
if (!string.IsNullOrEmpty(path) && (!NoImage || File.Exists(path)))
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
return decoder.Frames.FirstOrDefault();
}
}
else
return null;
}
catch (OutOfMemoryException ex)
{
MessageBox.Show("Insufficient memory to handle the process. Please try again later.", "Application alert");
return null;
}
catch (Exception ex)
{
// Error handling.
ShowMessages.AlertBox(ex.Message, MethodInfo.GetCurrentMethod().Name);
throw ex;
}
finally
{
decoder = null;
}
}
}
I need to know if there is any memory leak in the above code or is there any better way to load an image which match my requirements.

I had something similar to the same issue and solved by loading the image like this,
//Code:
Replace,
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
return decoder.Frames.FirstOrDefault();
}
With,
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(path);
bi.EndInit();
bi.Freeze();
return bi;
if needed make the bi object null in your finally block.

That code shouldn't cause a leak. But you should consider if you want to freeze the image. x.ImageSource.Freeze();
In what scenarios does freezing wpf objects benefit performace greatly
Also if you think you have a memory leak you should get a profiler.
Red Ant's profiler has saved me dozens of times Red Ant's .Net Memory Profiler
Seriously it's worth it, they probably have a free trial or something, but it can find many sources of leaks, like timers, events not being closed properly etc. Very helpful. If you don't like them then go for another solution but if your looking for leaks Visual Studio won't help you, you need a 3rd party solution.

Related

C# Image via Memorystream error (but only second time through)

I need to be able to convert an Image to a byte Array and then back again. I've followed the google results and it seemed to be working..except it doesn't :)
I am pretty sure it has something to do with the Memory stream (I'm getting that GDI+ error) but I'm using the "using" and I thought I was cleanup up after myself.
private void button1_Click(object sender, EventArgs e)
{
System.Drawing.Image myImage = null;
SetImage(ref myImage); //This one works
SetImage(ref myImage); // This call breaks on
//the first myImage.Save line
}
private void SetImage(ref System.Drawing.Image myImage)
{
try
{
byte[] myImageAsBytes = null;
//First time through we don't have an image already so we load from a file
if (myImage == null)
{
myImage = System.Drawing.Image.FromFile("C:\\temp\\test.jpg");
}
//Convert our Image to a Byte Array.
using (System.IO.MemoryStream myMemoryStream = new System.IO.MemoryStream())
{
myImage.Save(myMemoryStream, myImage.RawFormat);
myImageAsBytes = myMemoryStream.ToArray();
}
//Just debugging
myImage.Dispose();
myImage = null;
GC.Collect();
//And convert it back to an image.
//using (System.IO.MemoryStream myMemoryStream = new System.IO.MemoryStream(myImageAsBytes))
//{
// myImage = System.Drawing.Image.FromStream(myMemoryStream);
//}
System.IO.MemoryStream myMemoryStream2 = new System.IO.MemoryStream(myImageAsBytes);
myImage = System.Drawing.Image.FromStream(myMemoryStream2);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The documentation for Image.FromStream: https://msdn.microsoft.com/en-us/library/93z9ee4x(v=vs.110).aspx
Says:
You must keep the stream open for the lifetime of the Image.
When it comes to MemoryStreams there is actually no point in closing/disposing them. It's the only .NET class I can think of that is IDisposable that doesn't need disposing. It accidentally inherits IDisposable from System.IO.Stream, that's why.
So I would recommend that you just create your memory streams and let them be.
If you were to use Image.FromStream on a physical file, however, maybe you wont like to keep it open. What you could do in that case is to make a copy of the bitmap like this:
Bitmap safeBitmap;
using ( var stream = blah-blah-blah )
using ( var img = Image.FromStream(stream) )
safeBitmap = new Bitmap(img);
I know delphi but not C++. I think you have set myimage to null if myimage is an object, so tried to re-create. Or don't set it to null, just redraw on it or myimage.loadfromstream.
System.Drawing.Image myImage = null;
SetImage(ref myImage); //This one works
myimage :=create;
SetImage(ref myImage);
Its as simple as removing "ref" from everywhere
I suggest you read up what that key word is doing, you will then understand why its breaking it.
rather return the new image from the method.
i.e. change it from void to return the new image.
Update
below is a console version of your code basically unmodified at ALL
and it runs fine.... although i don't agree with the programming at ALL either.
Point is where are you getting an error if it seems to run with no errors
class Program
{
static void Main(string[] args)
{
Dosomething();
}
private static void Dosomething()
{
System.Drawing.Image myImage = null;
SetImage(ref myImage); //This one works
SetImage(ref myImage); // This call breaks on
}
private static void SetImage(ref System.Drawing.Image myImage)
{
try
{
byte[] myImageAsBytes = null;
//First time through we don't have an image already so we load from a file
if (myImage == null)
{
myImage = System.Drawing.Image.FromFile("C:\\temp\\test.jpg");
}
//Convert our Image to a Byte Array.
using (System.IO.MemoryStream myMemoryStream = new System.IO.MemoryStream())
{
myImage.Save(myMemoryStream, myImage.RawFormat);
myImageAsBytes = myMemoryStream.ToArray();
}
//Just debugging
myImage.Dispose();
myImage = null;
GC.Collect();
//And convert it back to an image.
//using (System.IO.MemoryStream myMemoryStream = new System.IO.MemoryStream(myImageAsBytes))
//{
// myImage = System.Drawing.Image.FromStream(myMemoryStream);
//}
System.IO.MemoryStream myMemoryStream2 = new System.IO.MemoryStream(myImageAsBytes);
myImage = System.Drawing.Image.FromStream(myMemoryStream2);
}
catch (Exception ex)
{
}
}
}

tiff viewer wpf application out of memory

I'm working on a Tiff Viewer project which deals with big 24bit colored tif files (>70MB).
Here is the code how I load the tif file:
TiffBitmapDecoder tbd = new TiffBitmapDecoder(new Uri(_strTiffPath),BitmapCreateOptions.DelayCreation, BitmapCacheOption.Default);
_frames = tbd.Frames;
I use the default cache option to prevent loading the whole file in memory.
My application has a side thumbnails view (vertical StackPanel with Image), and a Page view which views the selected thumbnail.
I load only visible thumbnails by this code:
internal static BitmapSource GetThumbAt(int i)
{
try
{
if (i >= _frames.Count)
return null;
BitmapFrame bf = _frames[i];
bf.Freeze();
return bf;
}
catch (Exception ex)
{
return null;
}
}
My problem is when I scroll down the thumbnails view to load the new visible pages, the memory load increases and I run into out of memory !
I tried to unload invisible pages (that were already loaded) but that doesn't help !
img.Source = null
Thank you helping me to figure this out.
I figured it out !
As mentioned in my previous comment, this article helped me a lot.
I just adapted it to my code and the memory is now unloading correctly.
Here are the modifications I made to my code:
internal static BitmapSource GetThumbAt(int i)
{
try
{
if (i >= _frames.Count)
return null;
BitmapFrame bf = _frames[i];
BitmapSource bs = bf.Clone() as BitmapSource; //make a copy of the original because bf is frozen and can't take any new property
BitmapUtility.AddMemoryPressure(bs);
return bs;
}
catch (Exception ex)
{
return null;
}
}

RestSharp - Download / Use image in WPF/Silverlight

I'm trying to use RestSharp to download an image from a WCF/Rest service. The result should be saved in a file and displayed in a Image control an a WPF/SL page.
private void GetImage()
{
RestClient _Client = new RestClient(BASE_URI);
RestRequest request = new RestRequest("/api/img/{FileName}");
request.AddParameter("FileName", "dummy.jpg", ParameterType.UrlSegment);
_Client.ExecuteAsync<MemoryStream>(
request,
Response =>
{
if (Response != null)
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = Response.Data;
String fn = String.Format(#"c:\temp\{0}.jpg", Guid.NewGuid().ToString());
System.IO.File.WriteAllBytes(fn,Response.Data.ToArray());
bitmapImage.EndInit();
img.Source = bitmapImage;
}
});
}
When I look in fiddler the image got downloaded correctly BUT no image is saved and nothing is displayd. There is no exception thown. ANy suggestions ?
UPDATED
A part of the problem turns out that RestSharp is not returning the expected memorystream. Moving to another methed and accessing the raw data in byte[] format solves part of the problem, saving the picutere to disk.
private void GetImage()
{
RestClient _Client = new RestClient(BASE_URI);
RestRequest request = new RestRequest("/api/img/{FileName}");
request.AddParameter("FileName", "dummy.jpg", ParameterType.UrlSegment);
_Client.ExecuteAsync(
request,
Response =>
{
if (Response != null)
{
byte[] imageBytes = Response.RawBytes;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(imageBytes);
bitmapImage.CreateOptions = BitmapCreateOptions.None;
bitmapImage.CacheOption = BitmapCacheOption.Default;
bitmapImage.EndInit();
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = String.Format(#"c:\temp\{0}.jpg", Guid.NewGuid().ToString());
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
this.Dispatcher.Invoke((Action)(() => { img.Source = bitmapImage; }));
;
}
});
}
Although calling this.dispatcher.Invoke I still get the error : The calling thread cannot acces this object because a different thread owns it.
As the BitmapImage is created in another thread than the UI thread, you also have to call Freeze to make it accessible in the UI thread.
Although not strictly necessary here, it is good practise to always dispose of any IDisposable objects, including MemoryStream. Therefore you will also have to set the BitmapImage.CacheOption property to OnLoad.
using (var memoryStream = new MemoryStream(imageBytes))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
}
The frozen BitmapImage is accessible in the UI thread:
Dispatcher.Invoke((Action)(() => img.Source = bitmapImage));
Are you checking for exceptions using the debugger? If an exception is thrown on a background task, it won't be rethrown on the caller code unless you access Task.Result or use the await operator.
My guess is that you don't have access to the location of C: you are writing to. That block of code seems unnecessary anyway though, you should be able to directly set the source of the image to the stream you have without writing it to disk. Try commenting the writing to drive piece of code out and see if that solves the issue.

Using httpwebrequest to get image from website to byte[]

I want to read the raw binary of a PNG file on a website and store it into a byte[], so far I have something like this:
Uri imageUri = new Uri("http://www.example.com/image.png");
// Create a HttpWebrequest object to the desired URL.
HttpWebRequest imgRequest = (HttpWebRequest)WebRequest.Create(imageUri);
using (HttpWebResponse imgResponse = (HttpWebResponse)imgRequest.GetResponse())
{
using (BinaryReader lxBR = new BinaryReader(imgResponse.GetResponseStream()))
{
using (MemoryStream lxMS = new MemoryStream())
{
lnBuffer = lxBR.ReadBytes(1024);
while (lnBuffer.Length > 0)
{
lxMS.Write(lnBuffer, 0, lnBuffer.Length);
lnBuffer = lxBR.ReadBytes(1024);
}
lnFile = new byte[(int)lxMS.Length];
lxMS.Position = 0;
lxMS.Read(lnFile, 0, lnFile.Length);
}
}
}
but I can't use GetResponse on Silverlight because it is not Asynchronous (I think that's the reason) so instead I should be using BeginGetResponse, but I'm not completely clear on how to go about it. This is what I have so far:
HttpWebResponse imgResponse = (HttpWebResponse)imgRequest.BeginGetResponse(new AsyncCallback(WebComplete), imgRequest);
using (imgResponse)
{
using (BinaryReader lxBR = new BinaryReader(imgResponse.GetResponseStream()))
{
/*Same*/
}
}
and
void WebComplete(IAsyncResult a)
{
HttpWebRequest req = (HttpWebRequest)a.AsyncState;
HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(a);
//...? Do I need something else here?
}
can someone explain me a little bit how to use the BeginGetResponse property and how do I use the AsyncCallback.
Thanks!
Note:
I'm new to silverlight and I've been following tutorials and borrowing from other responses here on StackOverflow:
(stackoverflow responce) what I need to do but not in silverlight
tutorial
WebRequest_in_Silverlight
is this valid Silverlight code?
HttpWebResponse imgResponse = (HttpWebResponse)imgRequest.BeginGetResponse(new AsyncCallback(WebComplete), imgRequest);
Got it working, want to post it here in case anyone needs it.
I need to get this image and then modify it (byte level) Silverlight didn't let me save the image directly to a WriteableBitmap and thus I had to get the image with a WebClient as a stream and then save it to a byte[]
this is how I get the image (I already have the specific Uri):
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(uri)
so when the image is loaded the wc_OpenReadCompleted method is called and it does something like this:
int lengthInBytes = Convert.ToInt32(e.Result.Length);
BinaryReader br = new BinaryReader(e.Result);
byte[] buffer = new byte[lengthInBytes];
using (br)
{
for (int i = 0; i < lengthInBytes; i++)
{
buffer[i] = br.ReadByte();
}
}
at the end the buffer[] has all the bytes of the image (what I wanted)
I'm sure there are better ways of doing this but this is working for me ! )
note: at some point I need to convert the byte[] to a BitmapImage (that was easier than expected):
//imageInBytes is a byte[]
if (imageInBytes != null)
{
MemoryStream rawBytesStream = new MemoryStream(imageInBytes);
BitmapImage img = new BitmapImage();
img.SetSource(rawBytesStream);
return img;
}
I hope this helps anyone.
Use the OpenReadAsync method of the WebClient object. Attach to the OpenReadCompleted event of the WebClient. Use the Stream provided by the Result property of the event args.
Consider setting AllowReadStreamBuffering, this will fill the entire stream before raising the OpenReadCompleted. In either case its likely you can use this stream to complete you real task rather than coping it into a MemoryStream.

Load image from file and print it using WPF... how?

I'm looking for an example of how to load an image from file and print it on a page using WPF. I'm having a hard time finding good information about WPF printing.
var bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri("");
bi.EndInit();
var vis = new DrawingVisual();
using (var dc = vis.RenderOpen())
{
dc.DrawImage(bi, new Rect { Width = bi.Width, Height = bi.Height });
}
var pdialog = new PrintDialog();
if (pdialog.ShowDialog() == true)
{
pdialog.PrintVisual(vis, "My Image");
}
If you want more control then PrintDialog.PrintVisual gives you you have to wrap your image in a FixedDocumet.
You can find simple code that creates a fixed document here:
http://www.ericsink.com/wpf3d/B_Printing.html
did playing around with this.
Tamir's answer is a great answer, but the problem is, that it's using the original size of the image.
write a solution myself , whitch doesn't stretch the image if it's smaller than the pagesize and bring the image if it's to large at the page.
It can be used for multiply copies and can be used with both orientations.
PrintDialog dlg = new PrintDialog();
if (dlg.ShowDialog() == true)
{
BitmapImage bmi = new BitmapImage(new Uri(strPath));
Image img = new Image();
img.Source = bmi;
if (bmi.PixelWidth < dlg.PrintableAreaWidth ||
bmi.PixelHeight < dlg.PrintableAreaHeight)
{
img.Stretch = Stretch.None;
img.Width = bmi.PixelWidth;
img.Height = bmi.PixelHeight;
}
if (dlg.PrintTicket.PageBorderless == PageBorderless.Borderless)
{
img.Margin = new Thickness(0);
}
else
{
img.Margin = new Thickness(48);
}
img.VerticalAlignment = VerticalAlignment.Top;
img.HorizontalAlignment = HorizontalAlignment.Left;
for (int i = 0; i < dlg.PrintTicket.CopyCount; i++)
{
dlg.PrintVisual(img, "Print a Large Image");
}
}
It only works for pictures from files with a path at the moment, but with a little bit work you can adept it and passing only a BitmapImage.
And it is useable with borderless prints (if your printer does support it)
Had to go the way with BitmapImage because it loads the default size of the image.
Windows.Controls.Image doesn't shows the right height and width if you're loading the image directly there.
I'm knowing, that the question is very old, but it was very difficult to found some useful information during searching this.
Hopefully my post will help other people.
Just load the image and apply it to a visual. Then use the PrintDialog to do the work.
...
PrintDialog printer = new PrintDialog();
if (printer.ShowDialog()) {
printer.PrintVisual(myVisual, "A Page Title");
}

Resources