Here is what I am trying to do: Thread B will download some images and store those images in a shared resource: Static ArrayList IMBuffer; thread A will take an image from IMBuffer and do something with it. The following is what I got:
Thread B:
// do something
System.Net.WebClient myWebClient = new System.Net.WebClient();
try
{ myWebClient.DownloadFile(pth, "BufferImg"); }
catch
{ // some stuff }
// add new dled image to IMBuffer
fs = new FileStream("BufferImg", FileMode.Open, FileAccess.Read);
Image img = Image.FromStream(fs);
lock (IMBuffer)
{ IMBuffer.Add(img); }
img.Dispose();
lock (IMRequest) { IMRequest.RemoveAt(0); }
myWebClient.Dispose();
//fs.Dispose();
// File.Delete("BufferImg");
// do something else
Thread A:
// do something
Image nextImg;
lock (IMBuffer)
{
nextImg = (Image)IMBuffer[0];
nextImg.Save(DLedIM);
}
// do something else
and here is the problem I am running to; since the images in IMBuffer was opened using a filestream, when the stream is disposed, the line: nextImg.Save(DLedIM); is causing "file is been used by another process" error. However if fs.Dispose(); line is commented out, then the program is locking up "BufferImg", as the result, it won't be able to download image to "BufferImg" after the first time. What should I do to fix this problem? Or is there a simpler way to accomplish what I am trying to do?
This should work:
byte[] buffer;
using (FileStream fs = new FileStream("BufferImg", FileMode.Open, FileAccess.Read))
{
buffer = new byte[fs.Length];
fs.Read(buffer, 0, (int)fs.Length);
}
using(Image img = Image.FromStream(new MemoryStream(buffer))
{
//...
}
Using a MemoryStream you avoid having to hold on to the FileStream - at this point the image does not have any connection at all to the file and hence you shouldn't have any problem with file locking.
Instead of implementing your own multithread producer/consumer workflow (and then having to debug it), why not just use an existing threadsafe (nay, concurrent) queue provided by .NET? Could save you a lot of trial and even more errors.
Details here: Thread-safe blocking queue implementation on .NET
Related
Can I get a GZipStream for a file on disk without writing the entire compressed content to temporary storage? I'm currently using a temporary file on disk in order to avoid possible memory exhaustion using MemoryStream on very large files (this is working fine).
public void UploadFile(string filename)
{
using (var temporaryFileStream = File.Open("tempfile.tmp", FileMode.CreateNew, FileAccess.ReadWrite))
{
using (var fileStream = File.OpenRead(filename))
using (var compressedStream = new GZipStream(temporaryFileStream, CompressionMode.Compress, true))
{
fileStream.CopyTo(compressedStream);
}
temporaryFileStream.Position = 0;
Uploader.Upload(temporaryFileStream);
}
}
What I'd like to do is eliminate the temporary storage by creating GZipStream, and have it read from the original file only as the Uploader class requests bytes from it. Is such a thing possible? How might such an implementation be structured?
Note that Upload is a static method with signature static void Upload(Stream stream).
Edit: The full code is here if it's useful. I hope I've included all the relevant context in my sample above however.
Yes, this is possible, but not easily with any of the standard .NET stream classes. When I needed to do something like this, I created a new type of stream.
It's basically a circular buffer that allows one producer (writer) and one consumer (reader). It's pretty easy to use. Let me whip up an example. In the meantime, you can adapt the example in the article.
Later: Here's an example that should come close to what you're asking for.
using (var pcStream = new ProducerConsumerStream(BufferSize))
{
// start upload in a thread
var uploadThread = new Thread(UploadThreadProc(pcStream));
uploadThread.Start();
// Open the input file and attach the gzip stream to the pcStream
using (var inputFile = File.OpenRead("inputFilename"))
{
// create gzip stream
using (var gz = new GZipStream(pcStream, CompressionMode.Compress, true))
{
var bytesRead = 0;
var buff = new byte[65536]; // 64K buffer
while ((bytesRead = inputFile.Read(buff, 0, buff.Length)) != 0)
{
gz.Write(buff, 0, bytesRead);
}
}
}
// The entire file has been compressed and copied to the buffer.
// Mark the stream as "input complete".
pcStream.CompleteAdding();
// wait for the upload thread to complete.
uploadThread.Join();
// It's very important that you don't close the pcStream before
// the uploader is done!
}
The upload thread should be pretty simple:
void UploadThreadProc(object state)
{
var pcStream = (ProducerConsumerStream)state;
Uploader.Upload(pcStream);
}
You could, of course, put the producer on a background thread and have the upload be done on the main thread. Or have them both on background threads. I'm not familiar with the semantics of your uploader, so I'll leave that decision to you.
New to this, i have been using serializing for sending image data constantly.
Meaning i save an image in a memorystream, and send it with serializing.
But i don´t understand if i am supposed to use Serializing or Networkstream.
The only thing i am doing is sending images in a loop.
I have been trying to get a Networkstream to work, but it doesn´t go my way, so here is my code. It sens only 1 image for some reason, when i use serializing it goes constantly.
private void Initialize()
{
NetSerializer.Serializer.Initialize(new Type[] { typeof(MemoryStream) });
tcplisten = new System.Net.Sockets.TcpListener(IPAddress.Any, 1700);
tcplisten.Start();
}
private void Listen()
{
try
{
using (TcpClient tt1 = tcplisten.AcceptTcpClient())
{
tt1.NoDelay = true;
while (checkBox1.Checked)
{
panel1.BackgroundImage = Image.FromStream(tt1.GetStream());
}
}
}
catch
{
}
}
And here is the send:
private void Send()
{
try
{
string process = Proccessname.Text;
using (TcpClient tcp = new TcpClient())
{
tcp.NoDelay = true;
while (capcon == true)
{
if (tcp.Connected)
{
PrintWindow(process, tcp.GetStream());
}
else
{
tcp.Connect(adress);
}
}
}
}
catch
{
}
}
what PrintWindow(process, tcp.GetStream()); does is simply, screenshot an a window (process = the name of the process), then where to save it (tcp.GetStream()) which makes it send and stream it (at least i think so).
It does work a bit, if i disconnect and connect etc, it will send an image, but it´s pretty random i think, not sure why i have to do that, i want it to keep sending as long as the loop is active.
(This works perfectly with Serialization and MemoryStream (save the bitmap in a memorystream and use it in Serialization) ).
So this is like 2 questions, depending on the answer of the first.
What is faster for my purpose, (Sending an image), Serialization or NetworkStream?
If NetworkStream is faster, how can i get it to work, what´s wrong with my code above.
Thanks
if (tcp.Connected)
{
MemoryStream ms = new MemoryStream();
PrintWindow(process, ms);
panel1.BackgroundImage = Image.FromStream(ms);
}
This works, so it is getting connected, and the data is getting saved (changed to MemoryStream instead of NetworkStream for this).
You say you get an image when you disconnect. That makes sense because you're reading the image directly from the stream. Since the stream is a NetworkStream, it has no idea when it has to end.
My suggestion is to add the size of the image buffer before sending the actual image buffer when receiving/sending it using a BinaryReader, like this:
BinaryReader reader = new BinaryReader(netStream);
while (true) {
// read how big the image buffer is
int ctBytes = reader.ReadInt32();
// read the image buffer into a MemoryStream
MemoryStream ms = new MemoryStream(reader.ReadBytes(ctBytes));
// get the image from the MemoryStream
Image img = Image.FromStream(ms);
// ...do something with img
}
And when sending:
BinaryWriter writer = new BinaryWriter(netStream);
while (someCondition) {
// get the image
Image img = SomeImage();
// save the image to a MemoryStream
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
// get the image buffer
byte[] buffer = new byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(buffer, 0, buffer.Length);
// write the size of the image buffer
writer.Write(buffer.Length);
// write the actual buffer
writer.Write(buffer);
}
I tweaked the code a bit, so just gonna write it here for other people.
Now sure if it´s better or worse though.
This is the sending part, i skip to convert to a byte[] and just use the MemoryStream.
using (MemoryStream ms = PrintWindow(process))
{
writer.Write(ms.Length);
writer.Write(ms.GetBuffer());
}
And here for the receiving part:
panel1.BackgroundImage = Image.FromStream(new MemoryStream(reader.ReadBytes((Int32)reader.ReadInt64())));
Put everything in one command.
Of course the BinaryWriter/Reader are there, but i didn´t put them here, as i am just showing how i changed the code.
I used Angelo Geels Code above*
EDIT: for some reason it only seems to work in .bmp, not sure why, doesn´t make sense to me.
EDIT 2:
This can be solved with:
writer.Write(ms.GetBuffer(),0,(int)ms.Length);
Think that´s the way.
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;`
I'm trying to do so but the program throws this exception:
An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file 'C:\Users\Roy\documents\visual studio 2010\Projects\Assignment3\Assignment3\bin\Debug\Images\Chrysanthemum.jpg' because it is being used by another process.
Is there a way to use it while it's open?
code:
if (imgAddMessage.Source != null)
{
BitmapImage src = (BitmapImage)imgAddMessage.Source;
if (!Directory.Exists("Images"))
{
Directory.CreateDirectory("Images");
}
FileStream stream = new FileStream("Images/" + imageName, FileMode.Create, FileAccess.ReadWrite);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
encoder.Save(stream);
stream.Close();
}
Two things.
1) Your posted code doesn't have a using statement around the FileStream nor does it call Dispose on it.
2) Whatever piece of code that is showing the picture that you are trying to save, must use FileAccess.Read, because only a single piece of code can hold a write lock on a file. Better yet, if it's your own program that is showing the file, just cache the image into memory and show the image based on the bytes in memory. Then you'll hold no locks on the file and can do whatever you want with it later on...
Just curious if your still having issues.
Did you try the following?
if (imgAddMessage.Source != null)
{
string imageDirectory = "pack://application:,,,/Images";
BitmapImage src = (BitmapImage)imgAddMessage.Source;
if (!Directory.Exists(imageDirectory))
Directory.CreateDirectory(imageDirectory);
using (FileStream stream = new FileStream(Path.Combine(imageDirectory, imageName), FileMode.Create, FileAccess.ReadWrite))
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
encoder.Save(stream);
}
}
And as #Marco said you must have read/write permission
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;
}