- Remove From My Forums
-
Question
-
I’m converting a byte array to an image and then save it to disk:
MemoryStream ms = new MemoryStream(mybytearray); Bitmap x = new Bitmap(Image.FromStream(ms, true, true), 100, 100); x.Save(@"c:image.png");
When the method is called multiple times I get the following exception:
System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at System.Drawing.Image.Save(String filename)
-
Edited by
Friday, February 3, 2012 10:19 PM
-
Edited by
Answers
-
Hi sir,
I have just modified your code a bit by including it in functions… I clicked on the button 101 times and no error…. working perfect
Please try this one :
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing; using System.IO; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public byte[] imageToByteArray(System.Drawing.Image imageIn) { MemoryStream ms = new MemoryStream(); imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); return ms.ToArray(); } public Image byteArrayToImage(byte[] byteArrayIn) { MemoryStream ms = new MemoryStream(byteArrayIn); Bitmap returnImage = new Bitmap(Image.FromStream(ms,true,true),100,100); returnImage.Save(@"C:DataRehan1.png"); return returnImage; } private void button1_Click(object sender, EventArgs e) { try { byte[] byt = imageToByteArray(Image.FromFile(@"C:DataRehan.jpg")); pictureBox1.Image = byteArrayToImage(byt); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } }
As you have included memory stream objects in the function, they have local scope so disposing will not be an issue.. with your code after you complete your processing you need to dispose all memory objects and bytes so this is a good alternative to use it
in functions.
Please mark as answer if this post helps you and unpropose it as answer if it does not help you.
Thanks
Rehan Bharucha — The Tech Robot
MCTS, MCITP, MCPD, MCT, MCC
-
Edited by
REHAN BHARUCHA
Saturday, February 4, 2012 5:32 AM -
Proposed as answer by
REHAN BHARUCHA
Saturday, February 4, 2012 1:40 PM -
Marked as answer by
FAndrei
Saturday, February 4, 2012 8:57 PM
-
Edited by
-
-
Proposed as answer by
REHAN BHARUCHA
Saturday, February 4, 2012 1:40 PM -
Marked as answer by
FAndrei
Saturday, February 4, 2012 8:57 PM
-
Proposed as answer by
Interesting…
Can you post some more (and a beet deeper) stack traces?
We faced a similar problems.
Long story…
Such errors never happens on .Net472.
After we switched to .NetCore 3.1, we have several such errors (during a year) only on Windows7 machine:
UnhandledException details: System.Runtime.InteropServices.ExternalException (0x80004005): A generic error occurred in GDI+.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawLine(Pen pen, Int32 x1, Int32 y1, Int32 x2, Int32 y2)
at ...FrmBase.WndProc(Message& m) in ...
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
WndProc
(override in Form class) is something like this:
protected override void WndProc(ref System.Windows.Forms.Message m) { base.WndProc(ref m); IntPtr hdc = WinApi.User32.GetWindowDC(m.HWnd); if (hdc == IntPtr.Zero) return; using (Graphics g = Graphics.FromHdc(hdc)) { g.DrawLine(ErrorPen, 0, 0, this.Width, 0); } WinApi.User32.ReleaseDC(m.HWnd, hdc); }
After we switched to .Net5, this error began to appear several times a day, but again only on windows7 machine.
Since I was not able to provide a 100% reproduction method, I did not report it.
Also on .Net5, several times (win 10) we got this:
UnhandledException details: System.Runtime.InteropServices.ExternalException (0x80004005): A generic error occurred in GDI+.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.FillRectangle(Brush brush, Int32 x, Int32 y, Int32 width, Int32 height)
at System.Windows.Forms.ToolStripTextBox.ToolStripTextBoxControl.WmNCPaint(Message& m)
at System.Windows.Forms.ToolStripTextBox.ToolStripTextBoxControl.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
at Interop.User32.CallWindowProcW(IntPtr wndProc, IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam)
at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
at System.Windows.Forms.Control.DefWndProc(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.TextBoxBase.WndProc(Message& m)
at System.Windows.Forms.TextBox.WndProc(Message& m)
at System.Windows.Forms.ToolStripTextBox.ToolStripTextBoxControl.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
at Interop.User32.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
and this one looks more similar to yours…
Judging from the user reports, I guess this crash mostly happens when the «desktop» is «hidden» or not shown (like lock screen when the machine enters sleep mode, login screen, the screen shown when you shutdown/reboot Windows).
We connect through RDP (connect/disconnect several times a day ~4-20), so most likely something like that.
When dealing with GDI+ in .Net (e.g. certain objects in System.Drawing namespace), we would sometimes get the following exception:
System.Runtime.InteropServices.ExternalException: «A generic error occurred in GDI+.»
Debugging this exception can be extremely frustrating as it can happen under many different circumstances (here and here), and the error message is too generic to provide any useful information.
This exception mostly occurs when trying to write over an image opened by an Image or Bitmap object. For example, the following code snippet will generate this error:
Image img = Image.FromFile(fileName);
try
{
img.Save(fileName); //throws exception!
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
The method listed above tries to open an image file, process the image (code not shown) and then save the image under the same file name. Unfortunately, due to the way GDI+ handles images, the image file was locked during the whole life time of the img object. And thus an exception was thrown from the underlying Windows API.
Based on Microsoft’s Knowledgebase article’s recommendations, we can prevent this exceptions in two ways (see the article for details):
1. Create a non-indexed image:
public void Method1()
{
Image img = Image.FromFile(fileName);
Bitmap bmp = img as Bitmap;
Graphics g = Graphics.FromImage(bmp);
Bitmap bmpNew = new Bitmap(bmp);
g.DrawImage(bmpNew, new Point(0, 0));
g.Dispose();
bmp.Dispose();
img.Dispose();
//code to manipulate bmpNew goes here.
bmpNew.Save(fileName);
}
2. Create an indexed image:
public void Method2()
{
Image img = Image.FromFile(fileName);
Bitmap bmp = img as Bitmap;
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
int byteCount = bmpData.Stride * bmpData.Height;
byte[] bytes = new byte[byteCount];
Marshal.Copy(bmpData.Scan0, bytes,0, byteCount);
bmp.UnlockBits(bmpData);
Bitmap bmpNew = new Bitmap(bmp.Width, bmp.Height);
BitmapData bmpData1 = bmpNew.LockBits(new Rectangle(new Point(), bmpNew.Size), ImageLockMode.ReadWrite, bmp.PixelFormat);
Marshal.Copy(bytes, 0, bmpData1.Scan0, bytes.Length);
bmpNew.UnlockBits(bmpData1);
bmp.Dispose();
//code to manipulate bmpNew goes here.
bmpNew.Save(fileName);
}
As can be seen, both methods utilize a secondary image to hold a copy of the original image and makes modifications there. The second image is effectively decoupled from the original one since an explicit copy is made in either method.
The source code of this article can be downloaded here.
Be Sociable, Share!
If you are trying to modify Bitmap, you may encounter the following GDI error which is very generic and does not provide any details. As the exception does not provide more details, it is very frustrating to figure out the root cause.
Bitmap.Save(): A generic error occurred in GDI+
2 Reasons Why This Generic GDI Error Occurred
GDI+ throws an error when it cannot save file. Following are 2 reasons why this error occurs.
- When you are initializing a Bitmap object from an image stored on hard disk, it creates a lock on the underlying image file. Due to the lock when you try to save and overwrite your modified bitmap, it throws this error.
- When you are saving a file, make sure the user has Write permission on the folder. This is important when you are getting this error on the Website because Website runs under restricted permissions.
There are three ways to fix this issue.
- Instead of overwriting the file, save a new file with a different name than the original file
- Only when the Bitmap object is disposed, the underlying lock on the file is removed. Once the lock is removed, you may overwrite the file. If you must overwrite the existing file, create a separate bitmap object from existing bitmap object. Now dispose the old bitmap object which will release the lock on the image file. Go ahead and make the needed changes in new bitmap object and save the new bitmap object with original image file name.
- Make sure the folder in which you are trying to save file is writable. In Web Application, the application pool or account which runs the Website must have write access to to the folder in order to save the file. For example if you are running Website under “DefaultAppPool”, you must give “IIS AppPoolDefaultAppPool” user “write” access to the folder.
Sample Code That Causes Error
Dim oBitmap As Bitmap
oBitmap = New Bitmap("c:\example.jpg")
Dim oGraphic As Graphics
oGraphic = Graphics.FromImage(oBitmap)
Dim oBrush As New SolidBrush(Color.Black)
Dim ofont As New Font("Arial", 8 )
oGraphic.DrawString("Some text to write", ofont, oBrush, 10, 10)
oBitmap.Save("c:\example.jpg",ImageFormat.Jpeg)
oBitmap.Dispose()
oGraphic.Dispose()
As shown in the above example, I am reading the bitmap, modifying it and overwriting it on the same file. As the process creates a lock on the underlying image, it will throw an exception.
Sample Code With Fix
Dim oBitmap As Bitmap
oBitmap = New Bitmap("c:\example.jpg")
Dim oGraphic As Graphics
' Here create a new bitmap object of the same height and width of the image.
Dim bmpNew As Bitmap = New Bitmap(oBitmap.Width, oBitmap.Height)
oGraphic = Graphics.FromImage(bmpNew)
oGraphic.DrawImage(oBitmap, New Rectangle(0, 0, _
bmpNew.Width, bmpNew.Height), 0, 0, oBitmap.Width, _
oBitmap.Height, GraphicsUnit.Pixel)
' Release the lock on the image file. Of course,
' image from the image file is existing in Graphics object
oBitmap.Dispose()
oBitmap = bmpNew
Dim oBrush As New SolidBrush(Color.Black)
Dim ofont As New Font("Arial", 8 )
oGraphic.DrawString("Some text to write", ofont, oBrush, 10, 10)
oGraphic.Dispose()
ofont.Dispose()
oBrush.Dispose()
oBitmap.Save("c:\example.jpg", ImageFormat.Jpeg)
oBitmap.Dispose()
As shown in the above example, as soon as I create bitmap from an image, I am disposing the original bitmap. It releases the lock on the file. Hence I am able to overwrite the same file with updated bitmap.
Raise your hand if you’ve loaded an image from a Resource (or a BLOB database field):
Bitmap bm2 = this.GetGlobalResourceObject(«Resources», «_BitMap») as Bitmap;
Response.ContentType = «image/jpeg»;
bm2.Save(Response.OutputStream, ImageFormat.Jpeg);
Response.End();
and finding out that you can’t save the image! You get a not so happy error:
A generic error occurred in GDI+.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.Source Error:
Line 41:
Line 42: Response.ContentType = "image/jpeg";
Line 43: bm2.Save(Response.OutputStream, ImageFormat.Jpeg);
Line 44: Response.End();
Line 45:
Source File: c:projects2005ArticlesInternationalizationTest.aspx.cs Line: 43
I know this isn’t the first time I’ve done this and scratched my head and go WTF. The bitmap’s there but it won’t save, not to the output stream or to disk (even with permissions for it).
So what gives? The documentation is not real clear on this, although MSDN has a little blurp on not saving to the same stream:
Remarks
You should avoid saving an image to the same stream that was used to construct it. Doing so might damage the stream.
I don’t think this applies here – the Bitmap resource comes from a database field’s raw byte stream into a memory stream from which the image is loaded in my custom Resource Provider out of a database. The original stream is long gone, but maybe that’s just the problem…
Whatever the problem is, the code above doesn’t work.
The solution is to create a second bitmap and basically copy the image into a new Bitmap object and then save to the outputstream:
Bitmap bm2 = this.GetGlobalResourceObject(«Resources», «_BitMap») as Bitmap;
Bitmap bm3 = new Bitmap(bm2);
Response.ContentType = «image/jpeg»;
bm3.Save(Response.OutputStream, ImageFormat.Jpeg);
bm2.Dispose();
bm3.Dispose();
Response.End();
Works but it really bites that this unobvious piece of code should be necessary especially since Bitmap objects are not exactly lightweight objects to load and keep around even for a short period of time.
Remember PNG Images are ‘special’
In addition to the above issue remember that you can’t save PNG images directly into the output stream because of some issues with that particular format. Instead you need to write the image first into a memory stream:
Bitmap bm2 = this.GetGlobalResourceObject(«Resources», «_BitMap») as Bitmap;
Bitmap bm3 = new Bitmap(bm2);
MemoryStream ms = new MemoryStream();
Response.ContentType = «image/png»;
bm3.Save(MemStream, System.Drawing.Imaging.ImageFormat.Png);
MemStream.WriteTo(Response.OutputStream);
bm2.Dispose();
bm3.Dispose();
Response.End();
Note that you still need to do the Bitmap swapping if the image is loaded a certain way (like from the ResourceProvider) as you can’t save to a memory stream without it either.
Forum Rules |
|