ObjectDisposedException из основного кода .NET - PullRequest
4 голосов
/ 10 мая 2010

У меня проблема с живым приложением.

(К сожалению, это посмертная отладка - у меня есть только эта трассировка стека. Я никогда не видел этого лично и не могу воспроизвести).

Я получаю это исключение:

message=Cannot access a disposed object.
Object name: 'Button'.
exceptionMessage=Cannot access a disposed object.
Object name: 'Button'.
exceptionDetails=System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Button'.
   at System.Windows.Forms.Control.CreateHandle()
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.Control.PointToScreen(Point p)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.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.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
exceptionSource=System.Windows.Forms
exceptionTargetSite=Void CreateHandle()

Похоже, что событие мыши достигает формы после ее удаления.

Обратите внимание, что ее нетмоего кода в этой трассировке стека.

Единственная странная (?) вещь, которую я делаю, это то, что я склонен довольно агрессивно использовать Dispose () Forms, когда я использую их с ShowModal ()"ниже).

РЕДАКТИРОВАТЬ: Просто чтобы уточнить, я использую C ++ - CLI, поэтому на самом деле я не вызываю Dispose () Я использую оператор удаления.Однако это то же самое, что вызов Dispose ().

Но я делаю это только после того, как ShowModal () вернулся (это должно быть безопасно, верно?), И только когда я закончу с формой.

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

Есть идеи?

Если бы вы даже могли предложить способы воспроизведения, это могло бы бытьполезно.

Джон


Помимо:

TBH Я никогда не понимал, строго ли необходим вызов Dispose () после Form.ShowDialog () - MSDNДокументы для ShowDialog (), на мой взгляд, немного неоднозначны.

Ответы [ 7 ]

1 голос
/ 17 января 2012

У меня была эта проблема с подклассом Button, который я написал. Для меня решением оказалось проверить свойство IsDisposed кнопки между моим вызовом base.OnMouseDown и остальным кодом в методе.

Примерно так:

protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mevent)
{
    base.OnMouseUp(mevent);

    if (this.IsDisposed) {
        return;
    }
}
1 голос
/ 19 июля 2010

Я нашел ваш вопрос при диагностике такого же странного следа стека в моем приложении. Вот трассировка стека, с которой мне пришлось работать:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'TextBox'.
  at System.Windows.Forms.Control.CreateHandle()
  at System.Windows.Forms.TextBoxBase.CreateHandle()
  at System.Windows.Forms.Control.get_Handle()
  at System.Windows.Forms.Control.set_CaptureInternal(Boolean value)
  at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
  at System.Windows.Forms.Control.WndProc(Message& m)
  at System.Windows.Forms.TextBoxBase.WndProc(Message& m)
  at System.Windows.Forms.TextBox.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.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Это не то же самое, что у вас, но у него есть некоторые из ключевых характеристик:

  • Дескриптор существует в начале, но не в конце трассировки стека.
  • Ни один из моего кода не виден в трассировке стека.

Я не думаю, что моя проблема такая же, как ваша, но я подумала, что поделюсь тем, что обнаружила, в надежде, что это даст вам некоторое представление. После некоторых экспериментов я смог воспроизвести мою проблему. Я подключил событие LostFocus к другому элементу управления, и в определенных ситуациях обработчик события LostFocus удалял некоторые элементы управления, которые больше не были релевантными.

Однако, если событие LostFocus было вызвано тем, что пользователь щелкнул один из элементов управления, который нужно удалить, я получаю трассировку стека выше. В моем случае Control.WndProc вызывает Control.WmKillFocus, который в конечном итоге вызывает мой обработчик событий LostFocus (другого элемента управления), я избавляюсь от элемента управления, по которому щелкнули, и затем вызывается Control.WmMouseDown.

Возможно, у вас возникла похожая ситуация, когда что-то срабатывает до WmMouseUp?

Использование .NET Reflector для просмотра того, какие события могут быть вызваны до того, как WmMouseUp может помочь вам отследить проблему.

1 голос
/ 10 мая 2010

Это очень странный стек вызовов. Кнопка была удалена, ее метод PointToScreen () воссоздает дескриптор. Но он не должен был получить сообщение мыши, если он был уничтожен. Только потоки могут действительно объяснить это.

Кроме того, к моменту появления сообщения о состоянии мыши ничего не должно было быть утилизировано. Предположительно это кнопка в диалоговом окне, которая закрывает ее. Убедитесь, что вы используете событие Click, а не событие MouseDown. Также убедитесь, что вы закрываете диалог, назначая его свойство DialogResult, а не вызывая Close (). Неудобно в C ++ / CLI, потому что он не хранит отдельные таблицы символов для типов и переменных.

Спросите пользователя, какие "улучшения" она запускает на этой машине.

1 голос
/ 10 мая 2010

Это происходит, если вы показываете форму после ее утилизации. (Я пробовал)

После вызова ShowDialog вы должны утилизировать форму, но только если вы не планируете ничего делать с этим экземпляром.

0 голосов
/ 26 ноября 2015

Похоже, что событие мыши достигает формы после ее удаления.

Да, действительно! Исключение ясно говорит: «Невозможно получить доступ к удаленному объекту, имя объекта: кнопка», что означает, что кнопка, которая уже была удалена, снова находится под давлением удаления, отсюда и исключение. Таким образом, на основании предоставленной вами трассировки стека, если вы наблюдаете нижнюю строку, становится очевидным, что второе повторное удаление на уже расположенной кнопке произошло во время события OnMouseUp кнопки.

  at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)

Поэтому я предлагаю создать пользовательскую кнопку, которая наследует элемент управления System.Windows.Forms.Button и переопределить событие OnMouseUp, при котором вы предотвращаете повторное удаление на основе свойства IsDisposed для Button, как показано ниже.

public class FlatSylteSystemButton : System.Windows.Forms.Button
{
    public FlatStyleSystemButton()
    {
           this.FlatStyle =FlatStyle.System;
    }

    protected override void OnMouseUp(MouseEventArgs mevent)
    {
        if(!this.IsDisposed)
        {
           base.OnMouseUp(mevent);
        }
    }
}

Надеюсь, это поможет!

0 голосов
/ 14 ноября 2010

У меня была такая же проблема, когда я вызывал метод Close() при нажатии кнопки для немодальной формы. Отладка сборки winforms привела меня к свойству кнопки FlatStyle. У тебя было button.FlatSyle = FlatStyle.System?

По какой-то причине иногда кнопка не получает WM_KILLFOCUS (OnLostFocus) во время закрытия и удаления формы. И если у вас есть button.FlatSyle = FlatStyle.System, это может привести к ObjectDisposedException с таким стеком вызовов.

0 голосов
/ 10 мая 2010

Почему бы вам не использовать свое воплощение формы внутри оператора using? Это позволило бы избежать необходимости вызывать утилизацию и гарантировать, что это будет сделано в правильное время.

например. (не проверено, сейчас нет доступа к компилятору)

using(FormX frm = new FormX())
{
   DialogResult res = frm.ShowDialog();
   // Do your other stuff after
}
...