A generic error occurred in gdi ошибка

I am working on to upload and save a thumbnail copy of that image in a thumbnail folder.

I am using following link:

http://weblogs.asp.net/markmcdonnell/archive/2008/03/09/resize-image-before-uploading-to-server.aspx

but

newBMP.Save(directory + "tn_" + filename);   

is causing exception «A generic error occurred in GDI+.»

I have tried to give permission on folder, also tried to use a new separate bmp object when saving.

Edit:

    protected void ResizeAndSave(PropBannerImage objPropBannerImage)
    {
        // Create a bitmap of the content of the fileUpload control in memory
        Bitmap originalBMP = new Bitmap(fuImage.FileContent);

        // Calculate the new image dimensions
        int origWidth = originalBMP.Width;
        int origHeight = originalBMP.Height;
        int sngRatio = origWidth / origHeight;
        int thumbWidth = 100;
        int thumbHeight = thumbWidth / sngRatio;

        int bannerWidth = 100;
        int bannerHeight = bannerWidth / sngRatio;

        // Create a new bitmap which will hold the previous resized bitmap
        Bitmap thumbBMP = new Bitmap(originalBMP, thumbWidth, thumbHeight);
        Bitmap bannerBMP = new Bitmap(originalBMP, bannerWidth, bannerHeight);

        // Create a graphic based on the new bitmap
        Graphics oGraphics = Graphics.FromImage(thumbBMP);
        // Set the properties for the new graphic file
        oGraphics.SmoothingMode = SmoothingMode.AntiAlias; oGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

        // Draw the new graphic based on the resized bitmap
        oGraphics.DrawImage(originalBMP, 0, 0, thumbWidth, thumbHeight);

        Bitmap newBitmap = new Bitmap(thumbBMP);
        thumbBMP.Dispose();
        thumbBMP = null;

        // Save the new graphic file to the server
        newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);

        oGraphics = Graphics.FromImage(bannerBMP);
        // Set the properties for the new graphic file
        oGraphics.SmoothingMode = SmoothingMode.AntiAlias; oGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

        // Draw the new graphic based on the resized bitmap
        oGraphics.DrawImage(originalBMP, 0, 0, bannerWidth, bannerHeight);
        // Save the new graphic file to the server
        bannerBMP.Save("~/image/" + objPropBannerImage.ImageId + ".jpg");


        // Once finished with the bitmap objects, we deallocate them.
        originalBMP.Dispose();

        bannerBMP.Dispose();
        oGraphics.Dispose();
    }

Cœur's user avatar

Cœur

36.9k25 gold badges193 silver badges262 bronze badges

asked Apr 7, 2013 at 13:18

donstack's user avatar

0

When either a Bitmap object or an Image object is constructed from a
file, the file remains locked for the lifetime of the object. As a
result, you cannot change an image and save it back to the same file
where it originated.
http://support.microsoft.com/?id=814675

A generic error occurred in GDI+, JPEG Image to MemoryStream

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

http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html

EDIT:
just writing from memory…

save to an ‘intermediary’ memory stream, that should work

e.g. try this one — replace

    Bitmap newBitmap = new Bitmap(thumbBMP);
    thumbBMP.Dispose();
    thumbBMP = null;
    newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);

with something like:

string outputFileName = "...";
using (MemoryStream memory = new MemoryStream())
{
    using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
    {
        thumbBMP.Save(memory, ImageFormat.Jpeg);
        byte[] bytes = memory.ToArray();
        fs.Write(bytes, 0, bytes.Length);
    }
}

Community's user avatar

answered Apr 7, 2013 at 13:26

NSGaga-mostly-inactive's user avatar

5

This error message is displayed if the path you pass to Bitmap.Save() is invalid (folder doesn’t exist etc).

answered May 16, 2014 at 13:06

Reg Edit's user avatar

Reg EditReg Edit

6,6611 gold badge35 silver badges46 bronze badges

2

    // Once finished with the bitmap objects, we deallocate them.
    originalBMP.Dispose();

    bannerBMP.Dispose();
    oGraphics.Dispose();

This is a programming style that you’ll regret sooner or later. Sooner is knocking on the door, you forgot one. You are not disposing newBitmap. Which keeps a lock on the file until the garbage collector runs. If it doesn’t run then the second time you try to save to the same file you’ll get the klaboom. GDI+ exceptions are too miserable to give a good diagnostic so serious head-scratching ensues. Beyond the thousands of googlable posts that mention this mistake.

Always favor using the using statement. Which never forgets to dispose an object, even if the code throws an exception.

using (var newBitmap = new Bitmap(thumbBMP)) {
    newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);
}

Albeit that it is very unclear why you even create a new bitmap, saving thumbBMP should already be good enough. Anyhoo, give the rest of your disposable objects the same using love.

answered Apr 7, 2013 at 14:04

Hans Passant's user avatar

Hans PassantHans Passant

919k145 gold badges1681 silver badges2525 bronze badges

0

In my case the bitmap image file already existed in the system drive, so my app threw the error «A Generic error occured in GDI+».

  1. Verify that the destination folder exists
  2. Verify that there isn’t already a file with the same name in the destination folder

Marcos Dimitrio's user avatar

answered Aug 14, 2014 at 7:56

Palanikumar's user avatar

PalanikumarPalanikumar

6,9204 gold badges40 silver badges51 bronze badges

Check your folder’s permission where the image is saved
Right cLick on folder then go :

Properties > Security > Edit > Add— select «everyone» and check Allow «Full Control»

Tolga Evcimen's user avatar

answered Dec 3, 2015 at 11:52

Asad's user avatar

AsadAsad

3594 silver badges5 bronze badges

1

I was facing the same issue A generic error occurred in GDI+ on saving while working on MVC app, I was getting this error because I was writing wrong path to save image, I corrected saving path and it worked fine for me.

img1.Save(Server.MapPath("/Upload/test.png", System.Drawing.Imaging.ImageFormat.Png);


--Above code need one change, as you need to put close brackets on Server.MapPath() method after writing its param.

Like this-

img1.Save(Server.MapPath("/Upload/test.png"), System.Drawing.Imaging.ImageFormat.Png);

Chandan's user avatar

Chandan

2171 gold badge3 silver badges17 bronze badges

answered Jan 14, 2016 at 7:21

Anjan Kant's user avatar

Anjan KantAnjan Kant

4,04040 silver badges39 bronze badges

GDI+ exceptions occured due to below points

  1. Folder access issue
  2. Missing properties of images

If folder issue — please provide access to application
If Missing properties then use below code

Code 1

using (Bitmap bmp = new Bitmap(webStream))
{
     using (Bitmap newImage = new Bitmap(bmp))
     {
         newImage.Save("c:temptest.jpg", ImageFormat.Jpeg);
     }
}

Code 2

using (Bitmap bmp = new Bitmap(webStream))
{

     using (Bitmap newImage = new Bitmap(bmp))
     {
        newImage.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
        Rectangle lockedRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        BitmapData bmpData = newImage.LockBits(lockedRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
        bmpData.PixelFormat = bmp.PixelFormat;
        newImage.UnlockBits(bmpData);
        using (Graphics gr = Graphics.FromImage(newImage))
         {
             gr.SmoothingMode = SmoothingMode.HighQuality;
             gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
             gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
         }

         foreach (var item in bmp.PropertyItems)
         {
             newImage.SetPropertyItem(item);
         }
         newImage.Save("c:temptest.jpg", ImageFormat.Jpeg);
    }
}

Different between code 1 and code 2

Code — 1 : it will just create image and can open it on normal image viewer

  • the image can’t open in Photoshop
  • Image size will be double

Code — 2 : to open image in image edition tools use code

by using code 1 it just create images but it not assign image marks.

answered Nov 27, 2019 at 12:06

Prem Kumar Badri's user avatar

I always check/test these:

  • Does the path + filename contain illegal characters for the given filesystem?
  • Does the file already exist? (Bad)
  • Does the path already exist? (Good)
  • If the path is relative: am I expecting it in the right parent directory (mostly bin/Debug ;-) )?
  • Is the path writable for the program and as which user does it run? (Services can be tricky here!)
  • Does the full path really, really not contain illegal chars? (some unicode chars are close to invisible)

I never had any problems with Bitmap.Save() apart from this list.

answered Mar 21, 2017 at 4:40

Xan-Kun Clark-Davis's user avatar

I had a different issue with the same exception.

In short:

Make sure that the Bitmap‘s object Stream is not being disposed before calling .Save .

Full story:

There was a method that returned a Bitmap object, built from a MemoryStream in the following way:

private Bitmap getImage(byte[] imageBinaryData){
    .
    .
    .
    Bitmap image;
    using (var stream = new MemoryStream(imageBinaryData))
    {
        image = new Bitmap(stream);
    }
    return image;
}

then someone used the returned image to save it as a file

image.Save(path);

The problem was that the original stream was already disposed when trying to save the image, throwing the GDI+ exeption.

A fix to this problem was to return the Bitmap without disposing the stream itself but the returned Bitmap object.

private Bitmap getImage(byte[] imageBinaryData){
   .
   .
   .
   Bitmap image;
   var stream = new MemoryStream(imageBinaryData))
   image = new Bitmap(stream);

   return image;
}

then:

using (var image = getImage(binData))
{
   image.Save(path);
}

answered Oct 8, 2018 at 9:28

Yan's user avatar

YanYan

4332 silver badges16 bronze badges

1

I got it working using FileStream, get help from these
http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html
http://csharpdotnetfreak.blogspot.com/2010/02/resize-image-upload-ms-sql-database.html

System.Drawing.Image imageToBeResized = System.Drawing.Image.FromStream(fuImage.PostedFile.InputStream);
        int imageHeight = imageToBeResized.Height;
        int imageWidth = imageToBeResized.Width;
        int maxHeight = 240;
        int maxWidth = 320;
        imageHeight = (imageHeight * maxWidth) / imageWidth;
        imageWidth = maxWidth;

        if (imageHeight > maxHeight)
        {
            imageWidth = (imageWidth * maxHeight) / imageHeight;
            imageHeight = maxHeight;
        }

        Bitmap bitmap = new Bitmap(imageToBeResized, imageWidth, imageHeight);
        System.IO.MemoryStream stream = new MemoryStream();
        bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
        stream.Position = 0;
        byte[] image = new byte[stream.Length + 1];
        stream.Read(image, 0, image.Length);
        System.IO.FileStream fs
= new System.IO.FileStream(Server.MapPath("~/image/a.jpg"), System.IO.FileMode.Create
, System.IO.FileAccess.ReadWrite);
            fs.Write(image, 0, image.Length);

answered Apr 7, 2013 at 14:53

donstack's user avatar

donstackdonstack

2,5483 gold badges29 silver badges44 bronze badges

For me it was a permission problem. Somebody removed write permissions on the folder for the user account under which the application was running.

answered Nov 17, 2014 at 9:18

Krisztián Balla's user avatar

Krisztián BallaKrisztián Balla

19k13 gold badges64 silver badges84 bronze badges

Create folder path image/thumbs on your hard disk => Problem solved!

answered Jul 24, 2014 at 16:50

Chưa biết's user avatar

Chưa biếtChưa biết

9198 silver badges6 bronze badges

    I used below logic while saving a .png format. This is to ensure the file is already existing or not.. if exist then saving it by adding 1 in the filename

Bitmap btImage = new Bitmap("D:\Oldfoldername\filename.png");
    string path="D:\Newfoldername\filename.png";
            int Count=0;
                if (System.IO.File.Exists(path))
                {
                    do
                    {
                        path = "D:\Newfoldername\filename"+"_"+ ++Count + ".png";                    
                    } while (System.IO.File.Exists(path));
                }

                btImage.Save(path, System.Drawing.Imaging.ImageFormat.Png);

answered Jun 16, 2016 at 13:52

Balaji Dinakaran's user avatar

I encountered this error while trying to convert Tiff images to Jpeg. For me the issue stemmed from the tiff dimensions being too large. Anything up to around 62000 pixels was fine, anything above this size produced the error.

answered Oct 20, 2016 at 8:59

Richtea88's user avatar

Richtea88Richtea88

1211 silver badge5 bronze badges

for me it was a path issue when saving the image.

int count = Directory.EnumerateFiles(System.Web.HttpContext.Current.Server.MapPath("~/images/savedimages"), "*").Count();

var img = Base64ToImage(imgRaw);

string path = "images/savedimages/upImages" + (count + 1) + ".png";

img.Save(Path.Combine(System.Web.HttpContext.Current.Server.MapPath(path)));

return path;

So I fixed it by adding the following forward slash

String path = "images/savedimages....

should be

String path = "/images/savedimages....

Hope that helps anyone stuck!

answered Jan 10, 2018 at 16:46

imBlue's user avatar

imBlueimBlue

1141 silver badge11 bronze badges

from msdn: public void Save (string filename); which is quite surprising to me because we dont just have to pass in the filename, we have to pass the filename along with the path for example: MyDirectory/MyImage.jpeg, here MyImage.jpeg does not actually exist yet, but our file will be saved with this name.

Another important point here is that if you are using Save() in a web application then use Server.MapPath() along with it which basically just returns the physical path for the virtual path which is passed in. Something like: image.Save(Server.MapPath("~/images/im111.jpeg"));

answered Mar 29, 2019 at 18:57

Fakhar Ahmad Rasul's user avatar

I use this solution

int G = 0;

private void toolStripMenuItem17_Click(object sender, EventArgs e)
{
  Directory.CreateDirectory("picture");// هذه العملية للرسم بدون ان يحذف بقية الرسومات
  G = G + 1;
  FormScreen();
  memoryImage1.Save("picture\picture" + G.ToString() + ".jpg");
  pictureBox1.Image = Image.FromFile("picture\picture" + G.ToString() + ".jpg");
}

LarsTech's user avatar

LarsTech

80.4k14 gold badges152 silver badges224 bronze badges

answered Jun 23, 2020 at 15:40

Raho's user avatar

1

The code below solved my problem

pictureBox1.Image=myImage;
  
Bitmap bmp = new Bitmap(pictureBox1.Image);
bmp.Save("C:\Users/super/Desktop/robin.jpg");     

desertnaut's user avatar

desertnaut

57.1k23 gold badges137 silver badges165 bronze badges

answered Jun 14, 2021 at 21:31

Robin's user avatar

  • Remove From My Forums
  • Question

  • Hello All,

    I was just going to play around by generating some bitmaps programatically.

    I started off with this simple example, expecting everything to go smoothly, but have run into a strange error.

    The following code is by no means good, just something simple and complete I would expect to work:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    namespace BitmapOutput
    {
        public partial class Form1 : Form
        {
            /// <summary>
            /// The picture i am drawing
            /// </summary>
            System.Drawing.Bitmap myBitmap;

            /// <summary>
            /// Graphics object for drawing
            /// </summary>
            System.Drawing.Graphics myGrafx;

            public Form1()
            {
                InitializeComponent();

                this.myBitmap = new Bitmap(800, 600);

                this.myGrafx =                 System.Drawing.Graphics.FromImage(this.myBitmap);

                this.DrawPicture();

                this.ShowPicture();

                this.SavePicture();
            }

            public void DrawPicture()
            {
                this.myGrafx.DrawEllipse(
                    new Pen(System.Drawing.Color.AliceBlue),                 new Rectangle(0, 0, 100, 100));

            }

            public void ShowPicture()
            {
                this.pictureBox1.Image = this.myBitmap;
            }

            public void SavePicture()
            {
                this.myBitmap.Save(«Output\out.bmp» ,                    System.Drawing.Imaging.ImageFormat.Bmp );
            }
        }
    }

    This runs fine until the SavePicture(…) function is called.

    I get the exception:
    «A generic error occurred in GDI+.»

    at the this.myBitmap.Save(…); line.

    Most likely there is some detail that I have overlooked, and I appreciate it if anyone could point out to me what I could do to fix it.

    But, I’d like to think that this code would work, it makes sense, and requires little effort, that should be one of the goals of .net

    Any help or ideas are greatly appreciated!

    P.S. how do I use ‘code’ tags?

Answers

  • it happens with me — its a minor bug which should *hopefully* be fixed in SP1 of .NET 2.0.

    sometimes this error occurs, sometimes it doesnt

    sometimes putting a Thread.Sleep() (for about 30 ms) helps it just before the save

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.

3 Ways to Fix Generic GDI Error

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.

Время на прочтение
6 мин

Количество просмотров 15K

Строго говоря именно это оригинальный текст статьи, а в блоге уже перевод. Здесь статья публикуется чуть позже и только потому получает бирку перевод.

В 2016 году, когда большинство программ выполняются в песочницах, из которых даже самый некомпетентный разработчик не сможет навредить системе, странно сталкиваться с проблемой, о которой дальше пойдет речь. Если честно, я надеялся, что она ушла в далекое прошлое вместе с Win32Api, но недавно я с ней столкнулся. До этого я лишь слышал жуткие байки

старых

более опытных разработчиков, что такое может быть.

Проблема

Утечка или использование слишком большого числа GDI объектов.

Симптомы:

  • В Task Manager на вкладке Details колонка GDI objects показывает угрожающие 10000(Если этой колонки нету, ее можно добавить, кликнув на заголовке таблицы правой кнопкой и выбрав пункт Select Columns)
  • При разработке на C# или другом языке выполняемом CLR полетит исключение, не блещущее конкретикой:

    Message: A generic error occurred in GDI+.
    Source: System.Drawing
    TargetSite: IntPtr GetHbitmap(System.Drawing.Color)
    Type: System.Runtime.InteropServices.ExternalException

    Также при определенных настройках или версии системы исключения может и не быть, но Ваше приложение не сможет нарисовать ни единого объекта.

  • При разработке на С/С++ все методы GDI вроде Create%SOME_GDI_OBJECT% стали возвращать NULL

Почему?

В системах семейства Windows может быть одновременно создано не более 65535 объектов GDI. Число, на самом деле, невероятно большое и ни при каком нормальном сценарии и близко не должно достигаться. На процесс установлено ограничение в 10000, которое хоть и можно изменить (в реестре изменить значение HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionWindowsGDIProcessHandleQuota в пределах от 256 до 65535), но Microsoft настоятельно не рекомендует увеличивать это ограничение. Если это сделать, то у одного процесса будет возможность положить систему настолько, что та даже не сможет нарисовать сообщение об ошибке. В этом случае система сможет ожить только после перезагрузки.

Как исправлять?

Если Вы живете в аккуратном управляемом CLR’ом мире, то вероятность 9 из 10, что у Вас в приложении обычная утечка памяти. Проблема хоть и неприятная, зато довольно обыденная и есть по меньшей мере дюжина отличных инструментов для ее поиска. Подробно останавливаться на этом не буду. Вам лишь будет нужно использовать любой профилировщик, чтобы посмотреть, не увеличивается ли число объектов-оберток над GDI ресурсами, это: Brush, Bitmap, Pen, Region, Graphics. Если это действительно так, то Вам повезло, можете закрывать вкладку со статьей.

Если не нашлась утечка объектов-оберток, то значит у Вас в коде есть прямое использование функций GDI и сценарий, при котором они не удаляются.

Что Вам будут советовать другие?

Официальное руководство от Microsoft или другие статьи по этому поводу, которые Вы найдете в интернете, будут советовать примерно следующее:

Найти все Create%SOME_GDI_OBJECT% и узнать, есть ли соответствующий ему DeleteObject(или ReleaseDC для HDC-объектов), а если и есть, то, возможно, существует сценарий, при котором он не вызовется.

Есть еще чуть улучшенная версия этого метода, она содержит дополнительный первый шаг:

Скачать утилиту GDIView. Она умеет показывать конкретное количество GDI объектов по типу и единственное, что настораживает, так это то, что сумма всех не соответствует значению в последней колонке. На это можно попробовать не обращать внимание, если она поможет хоть как-то сузить зону поиска.

Проект, над которым я работаю, имеет кодовую базу в более 9 миллионов строк и еще примерно столько же в third-party библиотеках, сотни вызовов функций GDI, размазанных по десяткам файлов. Я потратил много сил и кофе, прежде чем понял, что вручную просто невозможно это проанализировать ничего не упустив.

Что предложу я?

Если этот способ Вам покажется слишком длинным и требующим лишних телодвижений, значит, Вы еще не прошли все стадии отчаяния с предыдущим. Можете еще несколько раз попробовать прошлые шаги, но если не поможет, то не сбрасывайте со счетов этот вариант.

В поисках утечки я задался вопросом: “А где создаются те объекты, что утекают?” Было абсолютно невозможно поставить точки останова во всех местах, где вызываются функции API. К тому же не было полной уверенности, что это не происходит в .net framework или одной из third-party библиотек, которые мы используем. Несколько минут гугления привели меня к утилите Api Monitor, которая позволяла логировать и отлаживать вызовы любых системных функций. Я без труда нашел список всех функций, порождающих GDI объекты, честно нашел их и выбрал в Api Monitor’е, после чего установил точки останова.

После чего запустил процесс на отладку в Visual Studio, а здесь выбрал его в дереве процессов. Первая точка останова сработала мгновенно:

Вызовов было слишком много. Я быстро понял, что захлебнусь в этом потоке и нужно придумать что-то еще. Я снял точки останова с функций и решил посмотреть лог. Это были тысячи и тысячи вызовов. Стало очевидно, что их не проанализировать вручную.

Задача: Найти те вызовы функций GDI, которым не соответствует удаление. В логи присутствует все необходимое: список вызовов функций в хронологическом порядке, их возвращаемые значения и параметры. Получается, что мне нужно взять возвращаемое значение функции Create%SOME_GDI_OBJECT% и найти вызов DeleteObject с этим значением в качестве аргумента. Я выделил все записи в Api Monitor, вставил в текстовый файл и получил что-то вроде CSV с разделителем TAB. Запустил VS, где думал написать программу, чтобы попарсить это, но, прежде чем она загрузилась, мне пришла в голову идея получше: экспортировать данные в базу и написать запрос, чтобы выгрести то, что меня интересует. Это был правильный выбор, потому что позволил очень быстро задавать вопросы и получать на них ответы.

Есть множество инструментов, чтобы импортировать данные из CSV в базу, потому не буду на этом останавливаться (mysql, mssql, sqlite).

У меня получилась вот такая таблица:

-- mysql code
CREATE TABLE apicalls (
  id int(11) DEFAULT NULL,
  `Time of Day` datetime DEFAULT NULL,
  Thread int(11) DEFAULT NULL,
  Module varchar(50) DEFAULT NULL,
  API varchar(200) DEFAULT NULL,
  `Return Value` varchar(50) DEFAULT NULL,
  Error varchar(100) DEFAULT NULL,
  Duration varchar(50) DEFAULT NULL
)

Написал функцию mysql, чтобы получать дескриптор удаляемого объекта из вызова апи:

CREATE FUNCTION getHandle(api varchar(1000))
  RETURNS varchar(100) CHARSET utf8
BEGIN
DECLARE start int(11);
DECLARE result varchar(100);
SET start := INSTR(api,','); -- for ReleaseDC where HDC is second parameter. ex: 'ReleaseDC ( 0x0000000000010010, 0xffffffffd0010edf )'
IF start = 0 THEN
  SET start := INSTR(api, '(');
END IF;
SET result := SUBSTRING_INDEX(SUBSTR(api, start + 1), ')', 1);
RETURN TRIM(result);
END

И наконец запрос, который найдет все текущие объекты:

SELECT creates.id, creates.handle chandle, creates.API, dels.API deletedApi
FROM (SELECT a.id, a.`Return Value` handle, a.API FROM apicalls a WHERE a.API LIKE 'Create%') creates
  LEFT JOIN (SELECT
      d.id,
      d.API,
      getHandle(d.API) handle
    FROM apicalls d
    WHERE API LIKE 'DeleteObject%'
    OR API LIKE 'ReleaseDC%' LIMIT 0, 100) dels
    ON dels.handle = creates.handle
WHERE creates.API LIKE 'Create%';

(Строго говоря, он просто найдет все вызовы Delete на все вызовы Create)


На рисунке сразу видны вызовы, на которые так и не нашлось ни одного Delete.

Остался последний вопрос: Как найти откуда вызываются эти методы в контексте моего кода? И здесь мне помог один хитрый трюк:

  1. Запустить приложение на отладку в VS.
  2. Найти его в Api Monitor и выбрать.
  3. Выбрать нужную функцию Api и поставить точку останова.
  4. Терпеливо нажимать “Далее”, пока она не вызовется с интересующими параметрами. (Как же не хватала conditional breakpoints из vs
  5. Когда дойдете до нужного вызова, перейти в VS и нажать break all.
  6. Отладчик VS будет остановлен в месте, где создается утекающий объект и останется лишь найти, почему он не удаляется.


(Код написан исключительно для примера)

Резюме:

Алгоритм длинный и сложный, в нем задействовано много инструментов, но мне он дал результат значительно быстрее, чем тупой поиск ошибок по огромной кодовой базе.

Вот он, для тех кому было лень читать или кто уже забыл с чего все начиналось, пока читал:

  1. Поискать утечки памяти объектов-оберток GDI
  2. Если они есть, устранить и повторить первый шаг.
  3. Если их нет, то поискать вызовы функций апи напрямую.
  4. Если их немного, то поискать сценарий, при котором объект может не удаляться.
  5. Если их много или не получается отследить, то нужно скачать Api Monitor и настроить на логирование вызовов GDI функций.
  6. Запустить приложение на отладку в VS
  7. Воспроизвести утечку (это проинициализирует программу, что бы кешируемые объекты, не мазолили глаза в логе).
  8. Подключится Api Monitor’ом.
  9. Воспроизвести утечку.
  10. Скопировать лог в текстовый файл, импортировать в любую базу, что есть под рукой (скрипты в статье для mysql, но без труда адаптируются под любую РСУБД)
  11. Сопоставить Create и Delete методы (SQL-скрипт есть выше в этой статье), найти те, на которые нет вызов Delete
  12. Установить в Api Monitor точку останова на вызов нужного метода.
  13. Нажимать continue до тех пор, пока метод не вызовется с нужными параметрами. Плакать из-за отсутствия conditional breakpoints.
  14. Когда метод вызовется с нужными параметрами, нажать Break All в VS.
  15. Найти, почему этот объект не удаляется.

Очень надеюсь, что эта статья сэкономит кому-то много время и будет полезной.

When you try to modify a bitmap object, save or upload an image to a web server you may get the error message A generic error occurred in GDI+. The error can occur regardless of the application in use. This post provides the most applicable solutions affected users can apply to resolve the issue.

A generic error occurred in GDI+

What is GDI+?

Windows GDI+ provides two-dimensional vector graphics, imaging, and typography, acting as an intermediate layer between applications and device drivers for rendering two-dimensional graphics, images, and text. GDI+ improves on Windows Graphics Device Interface (GDI) (the graphics device interface included with earlier versions of Windows) by adding new features and optimizing existing ones. In other words, GDI+ is a graphical subsystem of Windows that consists of an application programming interface (API) to display graphics and formatted text on both video display and printer.

The A generic error occurred in the GDI+ error is very generic as the exception does not provide any details. However, if you’re faced with this issue, the suggestions provided below should help you resolve the issue depending on your case scenario or the task you performed.

1] Reboot the destination environment and then repeat the action you were performing. This worked for affected users deploying or moving application(s) from acceptance to prod.

2] This error can occur due to a ‘lock’ being set when you try to save and overwrite your modified bitmap because when you initialize a Bitmap object from an image stored on a hard disk, it creates a lock on the underlying image file. In other words, when either a Bitmap object or an Image object is constructed from a file, the file remains locked for the lifetime of the object.  As a result, you cannot change an image and save it back to the same file where it originated. In this case, to resolve the issue, do either of the following:

  • Instead of overwriting the file, save a new file with a different name than the original file.
  • If you must overwrite the existing file, create a separate bitmap object from the existing bitmap object, then dispose of the old bitmap object which will release the lock on the image file. Now, you can make the needed changes in the new bitmap object and save the new bitmap object with the original image file name.
  • Save to an ‘intermediary’ memory stream. The Memory /FileStream vs bitmap.Save(...) code, although counter-intuitive, should work with minor variations to account for any particular method call.

Note that it’s only when the Bitmap object is disposed of, that the underlying lock on the file is removed – once the lock is removed, you may overwrite it.

Read: Unblock or delete locked or protected files on Windows using ThisIsMyFile

3] You need to dispose of your images because if you rely on the garbage collector to find these in time and release the unmanaged GDI resources, you’re likely to get an exception. In addition, make a note of the following points when writing your code:

  • ConvertTo It seems like a waste, just cast if you need to.
  • If you need to combine paths, use Path.Combine.
  • Use string interpolation instead of plus’ing strings together.
  • If you need to escape a character like a backslash, use Verbatim String Literal @”…”.
  • Always favor using the using statement which never forgets to dispose of an object, even if the code throws an exception.

Read: Best Programming Principles & Guidelines all Programmers should follow

4] No Write permission on the folder when you are saving a file especially if you get the error on the website because the website runs under restricted permissions. In Web Application, the application pool or account which runs the website must have write access to the folder to save the file – so make sure the folder in which you are trying to save the file is writable. For example, if you are running a website under DefaultAppPool, you must assign IIS AppPoolDefaultAppPool user write access to the folder.

Read: Website asking for permission to store data on computer for offline use

5] If you’re running Azure, and you get the A generic error occurred in GDI+ while opening a bitmap already only on a remote web server, and not on your local test machine, it could be that the GDI+ present on Azure IIS servers can’t handle new format BMPs generated by GIMP. If this is the case for you, to resolve the issue, do any of the following:

  • Resave BMP with Paint.
  • Use an uncompressed PNG instead.
  • Save as 24bit BMP with GIMP. This method will produce bad quality.

Read: Paint 3D not saving; Does not display Export option

6] This error message is displayed if the path you pass to Bitmap.Save() is invalid (folder doesn’t exist etc). Simply put, you’re writing the wrong path to save the image. In this case, make sure to correct the saving path and that your path does include the filename as well.

7] In the case the bitmap image file already existed in the system drive, and your app threw the error, do the following:

  • Verify that the destination folder exists.
  • Verify that there isn’t already a file with the same name in the destination folder.
  • Check your folder’s permission where the image is saved on your local drive. To do this, right-click on the folder, then navigate to Properties > Security > Edit > Add – select Everyone and check Allow Full Control.

Read: How to take full Ownership of Files & Folders in Windows

I hope you find this post helpful!

How to solve A generic error occurred in GDI+ in C#?

If you get A generic error occurred in GDI+ in C#, it could be due to the bitmap image file you are trying to save already existing on your system drive. In this case, you can verify that the destination folder exists and that there isn’t already a file with the same name in the destination folder.

Read: A JavaScript error occurred in the main process message on Windows

What is a GDI+ error?

GDI+ throws an error when it cannot save files usually due to two most common reasons. One of the reasons is, that when you are initializing a Bitmap object from an image stored on a 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.

How do I fix GDI windows?

To fix the GDI+ Windows error on your device, see if the following suggestions work:

  • Run SFC scan in elevated command prompt.
  • Run the Power Troubleshooter.
  • Update the graphics card driver.
  • Check for Windows Update.
  • Start the computer in clean boot.

Read: Fix gdi32full.dll not found or missing error on Windows

What is GDI on my laptop?

GDI allows application programmers to display information on a screen or printer without having to be concerned about the details of a particular display device. The application programmer makes calls to methods provided by GDI+ classes and those methods in turn make the appropriate calls to specific device drivers. GDI+ insulates the application from the graphics hardware, and it is this insulation that allows developers to create device-independent applications.

Does Windows still use GDI?

With the introduction of Windows XP, GDI was deprecated in favor of its successor, the C++-based GDI+ subsystem. While GDI+ is included with Windows XP and later, the GDI+ dynamic library can also be shipped with an application and used on older versions of Windows.

Read: Ntdll.dll, Advapi32.dll, Gdi32.dll files explained.

Возможно, вам также будет интересно:

  • A cache control header is missing or empty ошибка
  • A bytes like object is required not str ошибка
  • A bag s belts ошибка пежо 308
  • A bag s belts ошибка пежо 207
  • A bag s belts ошибка citroen c3

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии