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

The error «A generic error occurred in GDI+» in Asp.Net is a common issue that can occur when working with images or graphics in the application. This error typically indicates that there is a problem with the way that the application is attempting to manipulate or process an image. The root cause of the error can be due to various factors, including improper handling of the image data, incorrect file format, or insufficient memory resources. Regardless of the cause, there are several methods that can be used to resolve the issue and get your application back up and running.

Method 1: Verify Image File Format

The «A generic error occurred in GDI+» error occurs when there is a problem with the image file format. To fix this error, you can use the Image.FromStream method to verify the image file format before saving it. Here are the steps to do it:

  1. Create a new MemoryStream from the image file bytes:
byte[] imageBytes = File.ReadAllBytes(imageFilePath);
MemoryStream ms = new MemoryStream(imageBytes);
  1. Use the Image.FromStream method to create a new Image object from the MemoryStream:
Image img = Image.FromStream(ms);
  1. Check the image format using the img.RawFormat property:
if (img.RawFormat.Equals(ImageFormat.Jpeg))
{
    // Save the image as JPEG
}
else if (img.RawFormat.Equals(ImageFormat.Png))
{
    // Save the image as PNG
}
else if (img.RawFormat.Equals(ImageFormat.Gif))
{
    // Save the image as GIF
}
else
{
    // Unsupported image format
}
  1. Save the image using the appropriate format:
img.Save(outputFilePath, img.RawFormat);

Here is the complete code example:

byte[] imageBytes = File.ReadAllBytes(imageFilePath);
MemoryStream ms = new MemoryStream(imageBytes);
Image img = Image.FromStream(ms);

if (img.RawFormat.Equals(ImageFormat.Jpeg))
{
    // Save the image as JPEG
}
else if (img.RawFormat.Equals(ImageFormat.Png))
{
    // Save the image as PNG
}
else if (img.RawFormat.Equals(ImageFormat.Gif))
{
    // Save the image as GIF
}
else
{
    // Unsupported image format
}

img.Save(outputFilePath, img.RawFormat);

By verifying the image file format before saving, you can avoid the «A generic error occurred in GDI+» error in Asp.Net.

Method 2: Increase Memory Limits

If you are encountering the «A generic error occurred in GDI+» issue in your ASP.NET application, it is likely that you are running out of memory. To fix this issue, you can increase the memory limits of your application. Here are the steps to do this:

Step 1: Open your web.config file

<configuration>
  <system.web>
    <httpRuntime maxRequestLength="xxx" executionTimeout="xxx" />
  </system.web>
</configuration>

Step 2: Increase the maxRequestLength and executionTimeout values

You can increase the maxRequestLength and executionTimeout values to allow your application to use more memory. Here is an example:

<configuration>
  <system.web>
    <httpRuntime maxRequestLength="102400" executionTimeout="3600" />
  </system.web>
</configuration>

Step 3: Increase the memory limits in your code

You can also increase the memory limits in your code by setting the Bitmap object’s PixelFormat to Format32bppArgb. Here is an example:

using (Bitmap bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
    // your code here
}

Step 4: Dispose of your objects properly

Make sure to dispose of your objects properly to avoid memory leaks. Here is an example:

using (Bitmap bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
    // your code here
}
bitmap.Dispose();

By following these steps, you should be able to fix the «A generic error occurred in GDI+» issue in your ASP.NET application.

Method 3: Verify Image Dimensions

One way to fix the «A generic error occurred in GDI+» error in ASP.NET is to verify the image dimensions before saving or manipulating the image. This can be done using the System.Drawing.Image class and its Width and Height properties. Here is an example code snippet:

using System.Drawing;

// Load the image file
Image image = Image.FromFile("example.jpg");

// Verify the image dimensions
if (image.Width > 0 && image.Height > 0)
{
    // Perform image manipulation or save the image
    // ...
}
else
{
    // Handle the error
    // ...
}

// Dispose of the image object
image.Dispose();

This code loads an image file and checks its dimensions before performing any manipulation or saving. If the dimensions are valid, the code can proceed with the desired operations. If not, the error can be handled appropriately.

It’s important to note that the Image.FromFile method can also throw the «A generic error occurred in GDI+» error if the image file is not valid. Therefore, it’s a good practice to wrap this method call in a try-catch block and handle any exceptions that may occur.

Overall, verifying image dimensions is a simple yet effective way to prevent the «A generic error occurred in GDI+» error in ASP.NET.

Method 4: Use a Try-Catch Block

One way to handle the «A generic error occurred in GDI+» error in ASP.NET is to use a try-catch block. Here’s an example of how to do it:

try
{
    // Your code that uses GDI+ here
}
catch (Exception ex)
{
    // Handle the exception
    if (ex is System.Runtime.InteropServices.ExternalException && ex.Message.Contains("GDI+"))
    {
        // Handle the GDI+ error
        // For example, you could log the error or display a user-friendly message
    }
    else
    {
        // Handle other exceptions
    }
}

In the try block, you would put the code that uses GDI+ and might throw the error. In the catch block, you would handle the exception.

The first if statement checks if the exception is a System.Runtime.InteropServices.ExternalException (which is the type of exception that the «A generic error occurred in GDI+» error belongs to) and if the error message contains «GDI+». If both conditions are true, then you know that this is the GDI+ error you’re trying to handle.

You can then add your own code to handle the error. For example, you could log the error or display a user-friendly message to the user.

If the exception is not the GDI+ error, you can handle it in the else block. You might want to log the error or display a different message to the user, depending on the type of exception.

Remember to always include a catch block when working with GDI+ to handle any errors that might occur.

Method 5: Debug the Application

If you are facing the error «A generic error occurred in GDI+» in your Asp.Net application, you can use the following steps to debug the issue:

Step 1: Enable Debugging

To enable debugging, add the following line to your web.config file:

<configuration>
   <system.web>
      <compilation debug="true" />
   </system.web>
</configuration>

Step 2: Add Exception Handling

Add the following code to your application’s global.asax file to handle exceptions:

void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    if (ex is HttpUnhandledException)
    {
        ex = ex.InnerException;
    }
    if (ex is System.Runtime.InteropServices.ExternalException)
    {
        Response.Write("Error occurred: " + ex.Message);
        Server.ClearError();
    }
}

Step 3: Use Try-Catch Blocks

Use try-catch blocks to catch the exception and handle it gracefully. For example:

try
{
    // your GDI+ code here
}
catch (System.Runtime.InteropServices.ExternalException ex)
{
    // handle the exception here
}

Step 4: Check Image File Format

Make sure that the image file format is supported by GDI+. You can use the following code to check the format:

System.Drawing.Image img = System.Drawing.Image.FromFile("image.jpg");
if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
{
    // the image is a JPEG
}

Step 5: Check Image Size

Make sure that the image size is within the limits supported by GDI+. You can use the following code to check the size:

System.Drawing.Image img = System.Drawing.Image.FromFile("image.jpg");
if (img.Width > 800 || img.Height > 600)
{
    // the image is too large
}

By following these steps, you should be able to debug the «A generic error occurred in GDI+» issue in your Asp.Net application.

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 AppPool\DefaultAppPool 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.

smokehawker

0 / 0 / 8

Регистрация: 17.10.2012

Сообщений: 289

1

13.10.2016, 19:38. Показов 8624. Ответов 1

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

В сишарпе не силен, что не так?
ошибка:
An unhandled exception of type ‘System.Runtime.InteropServices.ExternalException’ occurred in System.Drawing.dll

Additional information: A generic error occurred in GDI+.
в этой строке:

C#
1
  tempImage.SaveAsFile(@"C:\full.png", ImageFormat.Png);

код:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Interactions;
using DeathByCaptcha;
using System.IO; // для работы с файлами
using System.Threading; // для пауз
using System.Net; // для скачки капчи
using System.Diagnostics; // для отслеживания процессов
 
namespace driver
{
    class Program
    {
        public static Bitmap CropImage(Bitmap source, Rectangle section)
        {
            Bitmap bmp = new Bitmap(section.Width, section.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
            return bmp;
        }
        static void Main(string[] args)
        {
            IWebDriver driver = new ChromeDriver();
 
            //replace with the page you want to navigate to
            string your_page = "https://www.google.com";
            driver.Navigate().GoToUrl(your_page);
 
            ITakesScreenshot ssdriver = driver as ITakesScreenshot;
            Screenshot screenshot = ssdriver.GetScreenshot();
 
            Screenshot tempImage = screenshot;
 
            tempImage.SaveAsFile(@"C:\full.png", ImageFormat.Png);
 
            //replace with the XPath of the image element
            IWebElement my_image = driver.FindElement(By.XPath("//*[@id="hplogo"]/canvas[1]"));
 
            Point point = my_image.Location;
            int width = my_image.Size.Width;
            int height = my_image.Size.Height;
 
            Rectangle section = new Rectangle(point, new Size(width, height));
            Bitmap source = new Bitmap(@"C:\full.png");
            Bitmap final_image = CropImage(source, section);
 
            final_image.Save(@"C:\image.jpg");
        }
    }
}



0



OwenGlendower

Администратор

Эксперт .NET

16306 / 12798 / 5056

Регистрация: 17.03.2014

Сообщений: 26,063

Записей в блоге: 1

13.10.2016, 20:19

2

smokehawker, попробуй указать другой путь. Например, папку «мои документы».

C#
1
tempImage.SaveAsFile(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "full.png"), ImageFormat.Png);



1



Время на прочтение
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_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota в пределах от 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. Найти, почему этот объект не удаляется.

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

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 AppPool\DefaultAppPool” 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.

Понравилась статья? Поделить с друзьями:
  • A fan is not operating correctly ошибка
  • A0ad ошибка бмв
  • A carpet is in the bedroom где ошибка
  • A0b0 ошибка mini
  • A ball an dolls найдите ошибки