ImageList выдает исключение Out of memory при добавлении изображений из базы данных SQL

У меня есть несколько баз данных SQL, в которых есть таблица, в которой хранятся изображения с использованием типа данных varbinary.

При нажатии кнопки он удалит все существующие изображения, кроме первых трех изображений из элемента управления ImageList, и добавит все новые изображения в ImageList в моей форме.

Существует TreeView, который использует этот ImageList.

Я получаю:

Исключение нехватки памяти

...если я нажму кнопку много раз в одной и той же базе данных.

Упрощенный код:

if(imageList1.Images.Count > 3)
{
    for (int i = imageList1.Images.Count - 1; i > 2; i--)
    {
       imageList1.Images.RemoveAt(i);      
    }
}
int counter = 0;
foreach (DataRow dr in dataset.Tables[0].Rows)
{
    if (dr["ImageField"] != DBNull.Value)
    {
        byte[] imageData = (byte[])dr["ImageField"];
        MemoryStream ms = new MemoryStream(imageData, 0, imageData.Length);
        Bitmap img = new Bitmap(ms);
        imageList1.Images.Add("Image" + counter, img);
        img.Dispose();
        ms.Dispose();
        counter++;
    }
}

Трассировки стека:

at System.Drawing.Graphics.FromHdcInternal(IntPtr hdc)
   at System.Drawing.Font.ToLogFont(Object logFont)
   at System.Drawing.Font.ToHfont()
   at System.Windows.Forms.Control.FontHandleWrapper..ctor(Font font)
   at System.Windows.Forms.OwnerDrawPropertyBag.get_FontHandle()
   at System.Windows.Forms.TreeView.CustomDraw(Message& m)
   at System.Windows.Forms.TreeView.WmNotify(Message& m)
   at System.Windows.Forms.TreeView.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.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at System.Windows.Forms.Control.SendMessage(Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.Control.ReflectMessageInternal(IntPtr hWnd, Message& m)
   at System.Windows.Forms.Control.WmNotify(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.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.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 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.TreeView.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.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.SendMessage(HandleRef hWnd, Int32 msg, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control.EndUpdateInternal(Boolean invalidate)
   at System.Windows.Forms.TreeView.ImageListChangedHandle(Object sender, EventArgs e)
   at System.EventHandler.Invoke(Object sender, EventArgs e)
   at System.Windows.Forms.ImageList.OnChangeHandle(EventArgs eventargs)
   at System.Windows.Forms.ImageList.ImageCollection.Add(Original original, ImageInfo imageInfo)
   at System.Windows.Forms.ImageList.ImageCollection.Add(String key, Image image)
   at Test.MyClass.LoadLibraryImageList()

Почему он выдает это исключение, когда использование памяти очень низкое?


person Jack Le    schedule 26.03.2019    source источник
comment
Предпочитайте обертывать использование объектов, реализующих IDisposable, в операторах using, а не явно вызывать Dispose.   -  person Cody Gray    schedule 26.03.2019
comment
Этот первый цикл не удаляет ни одно из этих изображений. Если их много и/или вы делаете это неоднократно, у вас могут закончиться ресурсы. Кроме того, это похоже на неправильное использование списка изображений.   -  person Ňɏssa Pøngjǣrdenlarp    schedule 26.03.2019
comment
@MakeStackOverflowGoodAgain Как избавиться от ненужных изображений? Я обновил свой вопрос, у меня есть TreeView, который использует этот ImageList, каждая база данных имеет разные изображения, как мне хранить изображения для моего TreeView?   -  person Jack Le    schedule 26.03.2019
comment
Похоже, вы также используете некоторые шрифты (владелец нарисовал TreeView?). На данный момент у вас, вероятно, закончились дескрипторы и/или объекты GDI. Если вы создаете одноразовый объект, вы должны избавиться от него.   -  person Jimi    schedule 26.03.2019
comment
@Jimi У меня есть класс MultiSelectTreeView, который расширяет класс TreeView.   -  person Jack Le    schedule 26.03.2019
comment
imageList1.Images.Add("Image" + counter, img); img.Dispose(); Я не уверен, что это хорошая идея - добавить куда-нибудь изображение, а затем Dispose его сразу.   -  person mjwills    schedule 26.03.2019
comment
imageList1.Images.RemoveAt(i); Я подозреваю, что именно здесь вы должны на самом деле Dispose отображать изображения - непосредственно перед их удалением.   -  person mjwills    schedule 26.03.2019
comment
Да, я что-то такое придумал. ImageList может быть не единственной проблемой. Он может быть даже не настоящим. Однако см. это примечание в Справочный код о ImageList и внутренних дескрипторах. На самом деле иногда это проблема. Вероятно, вам следует полностью избавиться от ImageList (ImageList переопределяет Dispose() и удаляет все изображения, которые он создает), а затем добавить обратно изображения, которые вы хотите сохранить. Какие еще ручки вы там используете, неизвестно.   -  person Jimi    schedule 26.03.2019
comment
См. переопределить Dispose() внутренний метод.   -  person Jimi    schedule 26.03.2019
comment
@Jimi Я следил за диспетчером задач - объекты GDI и заметил, что иногда он доходил до 10 000, когда я удалял изображения из ImageList   -  person Jack Le    schedule 26.03.2019
comment
10.000 ручек это смерть. Система попрощается с вашим приложением. На самом деле трудно (в управляемом коде) зайти так далеко. Вероятно, у вас повсюду протекают ручки. Начните с удаления изображения (в том же индексе) перед вызовом RemoveAt(), который просто изменит размер и скопирует базовый ArrayList. Но я бы проследил за приложением без этого ImageList, чтобы увидеть, есть ли у вас проблема в другом месте, а ImageList будет последней каплей.   -  person Jimi    schedule 26.03.2019
comment
Мое предыдущее предложение - избавиться от ImageList и добавить несколько изображений, которые вы хотите сохранить, - остается в силе.   -  person Jimi    schedule 26.03.2019


Ответы (2)


Вы должны удалять изображения при их удалении (см. эту статью для объяснение).

Кроме того, цикл while проще. Просто удалите последнее изображение в списке, пока не останется только 3.

void RemoveLastImage(ImageList imageList)
{
    var lastIndex = imageList.Images.Count - 1;
    var lastImage = imageList.Images[lastIndex];
    imageList.Images.RemoveAt(lastIndex);
    lastImage.Dispose();
}

while (imageList1.Images.Count > 3)
{
    RemoveLastImage(imageList1);
}
person John Wu    schedule 26.03.2019
comment
Спасибо за предложение цикла while, это чище. Однако это не решает мою проблему. Я думаю, что настоящая проблема может быть где-то еще. - person Jack Le; 26.03.2019

Похоже, ImageList — это только верхушка айсберга, настоящая проблема заключается в моем расширенном MultiSelectTreeView элементе управления. Прочитав этот вопрос, я добавлял GC.Collect() в свой код всякий раз, когда Я меняю шрифт TreeNode и вуаля, проблема решена.

Из предложений я также удаляю ненужные изображения из моего ImageList перед загрузкой новой партии, что, похоже, также помогает снизить количество моих GDI. Спасибо всем.

person Jack Le    schedule 26.03.2019