Win32Exception: ошибка при создании дескриптора окна (большое количество вложенных элементов управления)

Прежде всего, извините за мой английский, так как это не мой родной язык.

Проблема возникла в моем приложении .NET Windows Forms и по некоторым причинам воспроизводилась только на ПК Virtaul с Windows 7 (x64).

Я столкнулся с Win32Exception в одном месте моего приложения при изменении размера основной формы приложения, содержащей множество настраиваемых элементов управления, в том числе два сложенных друг с другом элемента управления вкладками и т. Д. Сообщение об исключении было Ошибка создания дескриптора окна. Потратив некоторое время, я нашел решение этой проблемы на сайте поддержки Microsoft. Это было очень похоже на мой случай. Qoute с сайта:

В приложениях .NET пристыкованные элементы управления иногда не изменяют размер при изменении размера их родителей, если они глубоко вложены. Эта проблема возникает как на 64-битных, так и на 32-битных платформах, но требует более глубокого уровня вложенности в 32-битных системах.

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

Предлагаемое решение - переопределить метод OnSizeChanged для элементов управления контейнера (например, Panels или TabControl (в моем случае)):

    protected override void OnSizeChanged(EventArgs e)
    {
        if (this.Handle != null)
        {
            BeginInvoke((MethodInvoker)(() => base.OnSizeChanged(e);));
        }
    }

Я успешно применил это решение в своем случае: создал настраиваемый TabControl (унаследованный от простого класса Winforms TabControl) и изменил экземпляр TabControl на свой экземпляр настраиваемого элемента управления. И проблема ушла! Но...

... Когда я запустил Build на машине сборки CCnet, у меня было много Win32Exception-s Error создания дескриптора окна в разных модульных тестах, но что наиболее интересно, не вместо моего проблемного элемента управления (содержащего настраиваемый объект TabControl) модульного теста! Случаи ошибок были разными, но они не были связаны с моим настраиваемым элементом управления. Когда я отменил изменения, все было в порядке, сборка была создана (модульные тесты выполнены успешно).

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

Кстати, на локальной машине все модульные тесты выполняются нормально в любых случаях (с исправлением или без).

Я потратил много времени на исследование этой проблемы, и теперь у меня есть только одно предположение: машина сборки ccnet выполняет все модульные тесты в одном процессе, и тесты не удаляют тестовые данные (объекты gdi) правильно (при разрыве), так как возникает ошибка Ошибка создания дескриптор окна обычно происходит, когда достигается предел разрешенных дескрипторов объекта gdi (10000).

Не могли бы вы поделиться своим профессиональным мнением? Заранее большое спасибо.


person Kos L.A.    schedule 08.05.2012    source источник


Ответы (3)


Не имеет прямого отношения к вашей ошибке, но доступ к свойству Handle приведет к созданию дескриптора окна, что иногда невозможно. Проверки ручки не следует выполнять так:

if (this.Handle != null)

вместо этого проверьте свойство IsHandleCreated:

if (this.IsHandleCreated)

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

person roken    schedule 08.05.2012
comment
Спасибо! Я внесу соответствующие изменения. У меня также установлен resharper, и он всегда отмечает мне строку if (this.Handle! = Null), показывающую, что это избыточное условие всегда истинно .... Честно говоря, я не понимаю, возможно ли, что изменение размера будет выполнено на элемент управления, у которого нет созданного дескриптора? Возможно ли такое только для утилизированных элементов управления? - person Kos L.A.; 09.05.2012
comment
Да, свойство размера можно изменить до создания дескриптора (что обычно происходит при отображении элемента управления); например, когда размер задается с помощью конструктора, он устанавливается в конструкторе элемента управления. - person roken; 09.05.2012

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

person jlew    schedule 08.05.2012
comment
Я не вдавался в подробности реализации модульных тестов, но я думаю, что они создают экземпляры форм, но не отображают их. Что я точно знаю, так это то, что объекты доступа используются для доступа к закрытым полям. Однако дескрипторы создаются для каждого экземпляра объекта gdi во время его создания, независимо от того, был ли он показан (отрисован) или нет. - person Kos L.A.; 09.05.2012

У меня была такая же проблема с глубоко вложенными элементами управления. Однако для правильного решения мне пришлось перезаписать и отложить SetBoundsCore

    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        if (this.IsHandleCreated)
        {
            this.BeginInvoke(
                             (MethodInvoker)delegate
                             {
                                 base.SetBoundsCore(x, y, width, height, specified);
                             });
        }
    }
person Markus Stoll    schedule 19.11.2015