Понимание этой ошибки
Расширение границ Windows: объекты USER и GDI - часть 1, Марк Руссинович:
https://blogs.technet.microsoft.com/markrussinovich/2010/02/24/pushing-the-limits-of-windows-user-and-gdi-objects-part-1/
Устранение этой ошибки
Вы должны быть в состоянии воспроизвести проблему. Вот один из способов записи шагов для этого https://stackoverflow.com/a/30525957/495455.
Самый простой способ выяснить, что создает столько дескрипторов, - это открыть TaskMgr.exe. В TaskMgr.exe вам нужно, чтобы столбцы USER Object, GDI Object и Handles были видны, как показано, для этого выберите View Menu> Select Columns:
Пройдите шаги, чтобы вызвать проблему, и наблюдайте, как количество объектов USER увеличивается примерно до 10000 или GDI Objects или Handles достигают своих пределов.
Когда вы видите увеличение объектов или дескрипторов (как правило, резко), вы можете остановить выполнение кода в Visual Studio, нажав кнопку Пауза.
Затем просто нажмите и удерживайте F10 или F11, чтобы просмотреть код, наблюдая, как резко возрастает число объектов / дескрипторов.
Лучший инструмент, который я нашел на данный момент, - это GDIView от NirSoft, он разбивает поля дескриптора GDI:
Я отследил его до этого кода, используемого при настройке ширины столбца DataGridViews:
If Me.Controls.ContainsKey(comboName) Then
cbo = CType(Me.Controls(comboName), ComboBox)
With cbo
.Location = New System.Drawing.Point(cumulativeWidth, 0)
.Width = Me.Columns(i).Width
End With
'Explicitly cleaning up fixed the issue of releasing USER objects.
cbo.Dispose()
cbo = Nothing
End If
Это трассировка стека:
в System.Windows.Forms.Control.CreateHandle () в
System.Windows.Forms.ComboBox.CreateHandle () в
System.Windows.Forms.Control.get_Handle () в
System.Windows.Forms.ComboBox.InvalidateEverything () в
System.Windows.Forms.ComboBox.OnResize (EventArgs e) в
System.Windows.Forms.Control.OnSizeChanged (EventArgs e) в
System.Windows.Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32
ширина, высота Int32, IntW clientWidth, Int32 clientHeight) в
System.Windows.Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32
ширина, высота Int32) при
System.Windows.Forms.Control.SetBoundsCore (Int32 x, Int32 y, Int32
ширина, высота Int32, указывается BoundsSpecified) в
System.Windows.Forms.ComboBox.SetBoundsCore (Int32 x, Int32 y, Int32
ширина, высота Int32, указывается BoundsSpecified) в
System.Windows.Forms.Control.SetBounds (Int32 x, Int32 y, Int32 ширина,
Высота Int32, указана BoundsSpecified) в
System.Windows.Forms.Control.set_Width (значение Int32)
Вот суть полезной статьи Фабриса , которая помогла мне определить пределы:
«Ошибка создания дескриптора окна»
Когда активно используется приложение Windows Forms, над которым я работаю для клиента, пользователи часто получают исключения «Ошибка создания дескриптора окна».
Помимо того, что приложение потребляет слишком много ресурсов, что является отдельной проблемой, которую мы уже решаем, у нас возникли трудности с определением того, какие ресурсы исчерпаны, а также каковы ограничения для этих ресурсов.
Сначала мы подумали о том, как следить за счетчиком «Ручки» в диспетчере задач Windows. Это потому, что мы заметили, что некоторые процессы, как правило, потребляют больше этих ресурсов, чем обычно. Однако этот счетчик не очень хороший, потому что он отслеживает ресурсы, такие как файлы, сокеты, процессы и потоки. Эти ресурсы называются объектами ядра.
Другие виды ресурсов, за которыми мы должны следить, - это объекты GDI и объекты пользователя. Вы можете получить обзор трех категорий ресурсов в MSDN.
Объекты пользователя
Проблемы создания окон напрямую связаны с объектами пользователя.
Мы попытались определить, каков предел с точки зрения пользовательских объектов, которые может использовать приложение.
Для каждого процесса существует квота в 10 000 пользовательских дескрипторов. Это значение может быть изменено в реестре, однако в нашем случае это ограничение не было настоящей задержкой показа.
ThДругое ограничение - 66 536 пользовательских дескрипторов на сеанс Windows. Этот предел теоретический. На практике вы заметите, что это не может быть достигнуто. В нашем случае мы получали страшное исключение «Ошибка создания окна» до того, как общее число пользовательских объектов в текущем сеансе достигло 11 000.
Настольная куча
Затем мы обнаружили, какой предел был настоящим преступником: это была «Куча рабочего стола».
По умолчанию все графические приложения интерактивного пользовательского сеанса выполняются в так называемом «рабочем столе». Ресурсы, выделенные для такого рабочего стола, ограничены (но настраиваются).
Примечание. Пользовательские объекты - это то, что занимает большую часть пространства памяти кучи рабочего стола. Это включает в себя окна.
Для получения дополнительной информации о Desktop Heap вы можете обратиться к очень хорошим статьям, опубликованным в блоге NTDebugging MSDN:
Какое реальное решение? Будь зеленым!
Увеличение кучи рабочего стола является эффективным решением, но оно не является окончательным. Реальное решение - потреблять меньше ресурсов (в нашем случае меньше оконных дескрипторов). Я могу догадаться, как вы можете быть разочарованы этим решением. Это действительно все, что я могу придумать ??
Ну, здесь нет большого секрета. Единственный выход - быть стройным. Наличие менее сложных интерфейсов - хорошее начало. Это хорошо для ресурсов, это также хорошо для удобства использования. Следующий шаг - избежать потерь, сохранить ресурсы и перерабатывать их!
Вот как мы это делаем в приложении моего клиента:
Мы используем TabControls и создаем содержимое каждой вкладки на лету, когда она становится видимой;
Мы используем расширяемые / складывающиеся области и снова заполняем их элементами управления и данными только при необходимости;
Мы высвобождаем ресурсы как можно скорее (используя метод Dispose). Когда регион свернут, можно очистить его дочерние элементы управления. То же самое для вкладки, когда она становится скрытой;
Мы используем шаблон проектирования MVP, который помогает сделать это возможным, поскольку он отделяет данные от представлений;
Мы используем механизмы компоновки, стандартные FlowLayoutPanel и TableLayoutPanel или пользовательские, вместо того, чтобы создавать глубокие иерархии вложенных панелей, групповых блоков и разделителей (сам пустой разделитель использует три дескриптора окна ...).
Вышесказанное - это всего лишь подсказки, что вы можете сделать, если вам нужно создать богатые экраны Windows Forms. Там нет сомнений, что вы можете найти другие подходы.
Первое, что вы должны сделать, по моему мнению, это создание ваших приложений на основе вариантов использования и сценариев. Это помогает отображать только то, что нужно в данный момент времени и для данного пользователя.
Конечно, еще одно решение - использовать систему, которая не использует дескрипторы ... Кто-нибудь из WPF?