Open Image from file, then release lock? - c#

I'm using the following line of code to open an Image from a file:
pictureBox1.Image = Image.FromFile("test.png");
I expect it to lock the file, load the image to memory, set pictureBox1.Image to the copy in memory, and release the lock. In reality, the lock won't go away until I Dispose() of the Image in memory. I can not release the lock on the file on the harddrive that I am no longer using until I get rid of the file in memory that I am using.
Microsoft's site mentions it in a C#-labeled article, but their solution is written in visual basic, which is useless to me.
In summary:
I want to set pictureBox1.Image to the image stored in "test.png", then let the user edit or delete "test.png" or whatever.

The approach with stream is not correct.
See here https://stackoverflow.com/a/8701748/355264
Correct code from above link:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}

Or better yet, use a using statement (the code below is otherwise copied from sylon's [deleted] post). This way if the Image.FromStream throws an exception, you can still be assured that the stream is immediately closed.
using (FileStream stream = new FileStream("test.png", FileMode.Open, FileAccess.Read))
{
pictureBox1.Image = Image.FromStream(stream);
}

You can also use a stream to read the image then close the stream.
FileStream stream = new FileStream("test.png", FileMode.Open, FileAccess.Read);
pictureBox1.Image = Image.FromStream(stream);
stream.Close();

The easiest ever way I found is to freeze the object that contains the Source (path to the file). All controls that can contain an image, seem to have a .Source which, if not null, it will lock the file it points to.
Now the trick is to change the Image control to a "read-only" state, which then unlocks the file.
My solution:
private Image CreatePreviewImage()
{
Image ReportImage = new Image();
Uri path = new Uri(#"C:\Folder\Image1.png");
if (File.Exists(path.OriginalString))
{
ReportImage.Name = "Report1";
ReportImage.Source = LoadImageFromFile(path);
}
return ReportImage;
}
public ImageSource LoadImageFromFile(Uri path)
{
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = path;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bitmap.DecodePixelWidth = 900;
bitmap.EndInit();
bitmap.Freeze(); //This is the magic line that releases/unlocks the file.
return bitmap;
}

talking open, read and release
StreamReader streamReader = new StreamReader("picture.png");
Bitmap tmpBitmap = (Bitmap)Bitmap.FromStream(streamReader.BaseStream);
streamReader.Close();
pictureBox1.Image = tmpBitmap;`

Related

C# Bitmap.Save() executes but no file created

Using .net Framework 4.8 I have a method to resize images to thumbnails. It seems to work OK as I can see the file properties after it’s resized and all looks good. However, when I execute the save method which should save the new thumbnail to a new folder, no files are found in the new folder. All paths are correct. And I can confirm that the source files have not been altered (as expected). Lastly I’m getting no errors. Any ideas what is going wrong?
This image’s original width is 600 and looking at the screenshot you can see the new width is 212, so this confirms we have a valid bitmap to save.
Thanks.
Like I have a bitmap like bmp Then Now I want to save it Like this way ....
BitmapImage bitmapimage;
using (MemoryStream memory = new MemoryStream())
{
bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
memory.Position = 0;
bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
}
imgQR1.Source = bitmapimage;
string filePath = #"C:\Demo_Project\Image\test.png";
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapimage));
using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
{
encoder.Save(fileStream);
}

ExternalException when saving .bmp to MemoryStream as .png

I'm converting a batch of .bmp's to .png. This is the relevant part of the code:
foreach (string path in files) {
using (fs = new FileStream(path, FileMode.Open)) bmp = new Bitmap(fs);
using (ms = new MemoryStream()) {
bmp.Save(ms, ImageFormat.Png);
bmp.Dispose();
png = Image.FromStream(ms);
}
png.Save(path);
}
At the line bmp.Save(ms, ImageFormat.Png); the following exception is thrown:
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
According to MSDN this means the image was either saved with the wrong format or to the same location it was read from. The latter is not the case. However, I don't see how I gave it the wrong format: on the same MSDN page an example is given where a .bmp is converted to a .gif in the same manner.
Does it have to do with me saving to a MemoryStream? This is done so that I can overwrite the original file with the converted one. (Note that the .bmp suffix is kept intentionally. This shouldn't be the problem, since the exception appears before the final file is saved.)
In the MSDN documentation of that Bitmap constructor it says:
You must keep the stream open for the lifetime of the Bitmap.
and that same remark can be found on Image.FromStream.
So your code should carefully handle the scope and lifetime of the streams it uses for each of those bitmaps/images.
Combining all that the following code handles those streams correctly:
foreach (string path in files) {
using (var ms = new MemoryStream()) //keep stream around
{
using (var fs = new FileStream(path, FileMode.Open)) // keep file around
{
// create and save bitmap to memorystream
using(var bmp = new Bitmap(fs))
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
}
}
// write the PNG back to the same file from the memorystream
using(var png = Image.FromStream(ms))
{
png.Save(path);
}
}
}

Deleting a file with File.Delete throws System.IO.IOException [duplicate]

I'm trying create a user perfil edit window, in this window has a Image control
When I selected a image file, it will show in this Image control and copy this file at my image folder, first time is all right, but second time, it show a error
"The process cannot access the file 'C:\1.jpg' because it is being used by another process."
I think it is because my Image control is using this file, so, I don't know what can I do
private void Select_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
if (od.ShowDialog() == true)
{
string imageLocal = #"C:/1.jpg";
File.Copy(od.FileName, imageLocal, true);
image1.Source = new BitmapImage(new Uri(imageLocal));
}
}
If you want to load and display an image, and keep the file amenable to operations in the file system (like reloading it or moving it to another directory), the Uri constructor will not work because (as you point out), the BitmapImage class hangs on to the file handle.
Instead, use a method like this...
private static BitmapImage ByStream(FileInfo info)
{ //http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1
try
{
if (info.Exists)
{
// do this so that the image file can be moved in the file system
BitmapImage result = new BitmapImage();
// Create new BitmapImage
Stream stream = new MemoryStream(); // Create new MemoryStream
Bitmap bitmap = new Bitmap(info.FullName);
// Create new Bitmap (System.Drawing.Bitmap) from the existing image file
(albumArtSource set to its path name)
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
// Save the loaded Bitmap into the MemoryStream - Png format was the only one I
tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)
bitmap.Dispose(); // Dispose bitmap so it releases the source image file
result.BeginInit(); // Begin the BitmapImage's initialisation
result.StreamSource = stream;
// Set the BitmapImage's StreamSource to the MemoryStream containing the image
result.EndInit(); // End the BitmapImage's initialisation
return result; // Finally, set the WPF Image component's source to the
BitmapImage
}
return null;
}
catch
{
return null;
}
}
This method takes a FileInfo and returns a BitmapImage which you can display and simultaneously move it to another directory or display it again.
A much simpler method, copied from another answer below, is this:
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
The method shown below loads a BitmapImage from file and immediately closes the file after loading. Note that it is necessary to set the BitmapCacheOption.OnLoad flag when the source stream is closed right after EndInit.
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); // just in case you want to load the image in another thread
return bitmapImage;
}
}
This code will work for any image format that is supported by WPF. When passing the image file content as stream to the StreamSource property, WPF will automatically create the appropriate decoder.
Very simple solution:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Copy(od.FileName, imageLocal, true);

Image file copy, is being used by another process

I'm trying create a user perfil edit window, in this window has a Image control
When I selected a image file, it will show in this Image control and copy this file at my image folder, first time is all right, but second time, it show a error
"The process cannot access the file 'C:\1.jpg' because it is being used by another process."
I think it is because my Image control is using this file, so, I don't know what can I do
private void Select_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
if (od.ShowDialog() == true)
{
string imageLocal = #"C:/1.jpg";
File.Copy(od.FileName, imageLocal, true);
image1.Source = new BitmapImage(new Uri(imageLocal));
}
}
If you want to load and display an image, and keep the file amenable to operations in the file system (like reloading it or moving it to another directory), the Uri constructor will not work because (as you point out), the BitmapImage class hangs on to the file handle.
Instead, use a method like this...
private static BitmapImage ByStream(FileInfo info)
{ //http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1
try
{
if (info.Exists)
{
// do this so that the image file can be moved in the file system
BitmapImage result = new BitmapImage();
// Create new BitmapImage
Stream stream = new MemoryStream(); // Create new MemoryStream
Bitmap bitmap = new Bitmap(info.FullName);
// Create new Bitmap (System.Drawing.Bitmap) from the existing image file
(albumArtSource set to its path name)
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
// Save the loaded Bitmap into the MemoryStream - Png format was the only one I
tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)
bitmap.Dispose(); // Dispose bitmap so it releases the source image file
result.BeginInit(); // Begin the BitmapImage's initialisation
result.StreamSource = stream;
// Set the BitmapImage's StreamSource to the MemoryStream containing the image
result.EndInit(); // End the BitmapImage's initialisation
return result; // Finally, set the WPF Image component's source to the
BitmapImage
}
return null;
}
catch
{
return null;
}
}
This method takes a FileInfo and returns a BitmapImage which you can display and simultaneously move it to another directory or display it again.
A much simpler method, copied from another answer below, is this:
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
The method shown below loads a BitmapImage from file and immediately closes the file after loading. Note that it is necessary to set the BitmapCacheOption.OnLoad flag when the source stream is closed right after EndInit.
public static BitmapImage LoadBitmapImage(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); // just in case you want to load the image in another thread
return bitmapImage;
}
}
This code will work for any image format that is supported by WPF. When passing the image file content as stream to the StreamSource property, WPF will automatically create the appropriate decoder.
Very simple solution:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Copy(od.FileName, imageLocal, true);

Image.Save(..) throws a GDI+ exception because the memory stream is closed

i've got some binary data which i want to save as an image. When i try to save the image, it throws an exception if the memory stream used to create the image, was closed before the save. The reason i do this is because i'm dynamically creating images and as such .. i need to use a memory stream.
this is the code:
[TestMethod]
public void TestMethod1()
{
// Grab the binary data.
byte[] data = File.ReadAllBytes("Chick.jpg");
// Read in the data but do not close, before using the stream.
Stream originalBinaryDataStream = new MemoryStream(data);
Bitmap image = new Bitmap(originalBinaryDataStream);
image.Save(#"c:\test.jpg");
originalBinaryDataStream.Dispose();
// Now lets use a nice dispose, etc...
Bitmap2 image2;
using (Stream originalBinaryDataStream2 = new MemoryStream(data))
{
image2 = new Bitmap(originalBinaryDataStream2);
}
image2.Save(#"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}
Does anyone have any suggestions to how i could save an image with the stream closed? I cannot rely on the developers to remember to close the stream after the image is saved. In fact, the developer would have NO IDEA that the image was generated using a memory stream (because it happens in some other code, elsewhere).
I'm really confused :(
As it's a MemoryStream, you really don't need to close the stream - nothing bad will happen if you don't, although obviously it's good practice to dispose anything that's disposable anyway. (See this question for more on this.)
However, you should be disposing the Bitmap - and that will close the stream for you. Basically once you give the Bitmap constructor a stream, it "owns" the stream and you shouldn't close it. As the docs for that constructor say:
You must keep the stream open for the
lifetime of the Bitmap.
I can't find any docs promising to close the stream when you dispose the bitmap, but you should be able to verify that fairly easily.
A generic error occurred in GDI+.
May also result from incorrect save path!
Took me half a day to notice that.
So make sure that you have double checked the path to save the image as well.
Perhaps it is worth mentioning that if the C:\Temp directory does not exist, it will also throw this exception even if your stream is still existent.
Copy the Bitmap. You have to keep the stream open for the lifetime of the bitmap.
When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI
public static Image ToImage(this byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
using (var image = Image.FromStream(stream, false, true))
{
return new Bitmap(image);
}
}
[Test]
public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
{
var imageBytes = File.ReadAllBytes("bitmap.bmp");
var image = imageBytes.ToImage();
image.Save("output.bmp");
}
I had the same problem but actually the cause was that the application didn't have permission to save files on C. When I changed to "D:\.." the picture has been saved.
You can try to create another copy of bitmap:
using (var memoryStream = new MemoryStream())
{
// write to memory stream here
memoryStream.Position = 0;
using (var bitmap = new Bitmap(memoryStream))
{
var bitmap2 = new Bitmap(bitmap);
return bitmap2;
}
}
This error occurred to me when I was trying from Citrix. The image folder was set to C:\ in the server, for which I do not have privilege. Once the image folder was moved to a shared drive, the error was gone.
A generic error occurred in GDI+. It can occur because of image storing paths issues,I got this error because my storing path is too long, I fixed this by first storing the image in a shortest path and move it to the correct location with long path handling techniques.
I was getting this error, because the automated test I was executing, was trying to store snapshots into a folder that didn't exist. After I created the folder, the error resolved
One strange solution which made my code to work.
Open the image in paint and save it as a new file with same format(.jpg). Now try with this new file and it works. It clearly explains you that the file might be corrupted in someway.
This can help only if your code has every other bugs fixed
It has also appeared with me when I was trying to save an image into path
C:\Program Files (x86)\some_directory
and the .exe wasn't executed to run as administrator, I hope this may help someone who has same issue too.
For me the code below crashed with A generic error occurred in GDI+on the line which Saves to a MemoryStream. The code was running on a web server and I resolved it by stopping and starting the Application Pool that was running the site.
Must have been some internal error in GDI+
private static string GetThumbnailImageAsBase64String(string path)
{
if (path == null || !File.Exists(path))
{
var log = ContainerResolver.Container.GetInstance<ILog>();
log.Info($"No file was found at path: {path}");
return null;
}
var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;
using (var image = Image.FromFile(path))
{
using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
{
using (var memoryStream = new MemoryStream())
{
thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here
var bytes = new byte[memoryStream.Length];
memoryStream.Position = 0;
memoryStream.Read(bytes, 0, bytes.Length);
return Convert.ToBase64String(bytes, 0, bytes.Length);
}
}
}
}
I came across this error when I was trying a simple image editing in a WPF app.
Setting an Image element's Source to the bitmap prevents file saving.
Even setting Source=null doesn't seem to release the file.
Now I just never use the image as the Source of Image element, so I can overwrite after editing!
EDIT
After hearing about the CacheOption property(Thanks to #Nyerguds) I found the solution:
So instead of using the Bitmap constructor I must set the Uri after setting CacheOption BitmapCacheOption.OnLoad.(Image1 below is the Wpf Image element)
Instead of
Image1.Source = new BitmapImage(new Uri(filepath));
Use:
var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;
See this: WPF Image Caching
Try this code:
static void Main(string[] args)
{
byte[] data = null;
string fullPath = #"c:\testimage.jpg";
using (MemoryStream ms = new MemoryStream())
using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
using (Bitmap bm = new Bitmap(tmp))
{
bm.SetResolution(96, 96);
using (EncoderParameters eps = new EncoderParameters(1))
{
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
}
data = ms.ToArray();
}
File.WriteAllBytes(fullPath, data);
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int j = 0; j < encoders.Length; ++j)
{
if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
return encoders[j];
}
return null;
}
I used imageprocessor to resize images and one day I got "A generic error occurred in GDI+" exception.
After looked up a while I tried to recycle the application pool and bingo it works. So I note it here, hope it help ;)
Cheers
I was getting this error today on a server when the same code worked fine locally and on our DEV server but not on PRODUCTION. Rebooting the server resolved it.
public static byte[] SetImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
public static Bitmap SetByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream, false);
mStream.Dispose();
return bm;
}

Categories

Resources