Понимание этой ошибки
Расширяя границы 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 увеличивается примерно до 10 000 или объекты или дескрипторы GDI достигают своих пределов.
Когда вы видите, что объект или дескрипторы увеличиваются (обычно резко), вы можете остановить выполнение кода в Visual Studio, нажав кнопку «Пауза».
Затем просто удерживайте F10 или F11, чтобы просмотреть код, наблюдая, когда количество объектов / дескрипторов резко возрастает.
Лучший инструмент, который я нашел до сих пор, - это GDIView от NirSoft, он разбивает поля GDI Handle:

Я отследил это до этого кода, который использовался при настройке местоположения и ширины столбцов поля со списком DataGridViews Filter:
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
В моем случае (выше) решением было явное удаление и очистка, которая устранила проблему с освобождением объектов USER.
Это трассировка стека:
в 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, Int32 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, над которым я работаю для клиента, активно используется, пользователи часто получают ошибку при создании исключений дескриптора окна.
Помимо того факта, что приложение потребляет слишком много ресурсов, что является отдельной проблемой, которую мы уже решаем, у нас возникли трудности с определением того, какие ресурсы были исчерпаны, а также каковы ограничения для этих ресурсов. Сначала мы подумали о том, чтобы следить за счетчиком Handles в диспетчере задач Windows. Это произошло потому, что мы заметили, что некоторые процессы, как правило, потребляют больше этих ресурсов, чем обычно. Однако этот счетчик не самый лучший, потому что он отслеживает такие ресурсы, как файлы, сокеты, процессы и потоки. Эти ресурсы называются Объектами ядра.
Другими видами ресурсов, за которыми мы должны следить, являются объекты GDI и пользовательские объекты. Вы можете получить обзор трех категорий ресурсов на MSDN.
Пользовательские объекты
Проблемы создания окна напрямую связаны с пользовательскими объектами.
Мы попытались определить, каков лимит с точки зрения пользовательских объектов, которые может использовать приложение. Существует квота в 10 000 пользовательских дескрипторов для каждого процесса. Это значение можно изменить в реестре, однако в нашем случае это ограничение не было реальной проблемой. Другой предел - 66 536 пользовательских дескрипторов на сеанс Windows. Это теоретический предел. На практике вы заметите, что до него нельзя добраться. В нашем случае мы получали ужасную ошибку при создании исключения дескриптора окна до того, как общее количество пользовательских объектов в текущем сеансе достигло 11000.
Куча рабочего стола
Затем мы выяснили, какое ограничение было настоящей причиной: это куча рабочего стола. По умолчанию все графические приложения интерактивного сеанса пользователя выполняются на так называемом рабочем столе. Ресурсы, выделяемые такому рабочему столу, ограничены (но настраиваются).
Примечание. Пользовательские объекты занимают большую часть памяти кучи рабочего стола. Сюда входят окна. Для получения дополнительной информации о куче рабочего стола вы можете обратиться к очень хорошим статьям, опубликованным в блоге NTDebugging MSDN:
Какое реальное решение? Будьте экологичны!
Увеличение кучи рабочего стола - эффективное решение, но не окончательное. Реальное решение - потреблять меньше ресурсов (в нашем случае меньше оконных дескрипторов). Я могу догадаться, насколько вы можете быть разочарованы этим решением. Неужели это все, что я могу придумать ?? Что ж, здесь нет большого секрета. Единственный выход - быть худым. Менее сложные пользовательские интерфейсы - хорошее начало. Это хорошо для ресурсов, это хорошо для удобства использования. Следующий шаг - избегать отходов, беречь ресурсы и перерабатывать их!
Вот как мы это делаем в моем клиентском приложении:
Мы используем TabControls и создаем содержимое каждой вкладки «на лету», когда оно становится видимым; Мы используем расширяемые / сворачиваемые области и снова заполняем их элементами управления и данными только при необходимости; Освобождаем ресурсы при первой возможности (с помощью метода Dispose). Когда регион свернут, можно очистить его дочерние элементы управления. То же самое для вкладки, когда она становится скрытой; Мы используем шаблон проектирования MVP, который помогает сделать это возможным, потому что он отделяет данные от представлений; Мы используем механизмы компоновки, стандартные FlowLayoutPanel и TableLayoutPanel или пользовательские, вместо создания глубоких иерархий вложенных панелей, GroupBoxes и Splitters (пустой разделитель занимает три оконных дескриптора ...). Вышеупомянутое - лишь намеки на то, что вы можете сделать, если вам нужно создать многофункциональные экраны Windows Forms. Несомненно, можно найти и другие подходы. На мой взгляд, первое, что вам следует сделать, - это создавать свои приложения на основе вариантов использования и сценариев. Это помогает отображать только то, что необходимо в данный момент и для данного пользователя.
Конечно, другим решением было бы использовать систему, которая не полагается на дескрипторы ... Кто-нибудь WPF?
person
Jeremy Thompson
schedule
25.10.2016