Не удается получить доступ к удаленному объекту - Как это исправить? - PullRequest
34 голосов
/ 27 августа 2008

В проекте VB.NET WinForms я получаю исключение

Невозможно получить доступ к удаленному объекту

при закрытии формы. Это происходит очень редко, и я не могу воссоздать его по требованию. Трассировка стека выглядит следующим образом:

Cannot access a disposed object. Object name: 'dbiSchedule'.
  at System.Windows.Forms.Control.CreateHandle()
  at System.Windows.Forms.Control.get_Handle()
  at System.Windows.Forms.Control.PointToScreen(Point p)
  at Dbi.WinControl.Schedule.dbiSchedule.a(Boolean A_0)
  at Dbi.WinControl.Schedule.dbiSchedule.a(Object A_0, EventArgs A_1)
  at System.Windows.Forms.Timer.OnTick(EventArgs e)
  at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

dbiSchedule - это управление расписанием от Dbi-tech. В форме есть таймер, который обновляет расписание на экране каждые несколько минут.

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


Хей! Спасибо за ответы на все вопросы. Мы останавливаем Timer в событии FormClosing и проверяем свойство IsDisposed в компоненте расписания перед его использованием в событии Timer Tick, но это не помогает.

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

Ответы [ 11 ]

19 голосов
/ 27 августа 2008

Попробуйте проверить свойство IsDisposed перед доступом к элементу управления. Вы также можете проверить его на событии FormClosing , если вы используете событие FormClosed.

Мы останавливаем таймер на Событие FormClosing, и мы проверяем IsDisposed недвижимость по графику компонент, прежде чем использовать его в таймере Отметьте событие, но это не поможет.

Вызов GC.Collect перед проверкой IsDisposed может помочь, но будьте осторожны с этим. Прочитайте эту статью Рико Мариани " Когда вызывать GC.Collect () ".

10 голосов
/ 27 августа 2008

Похоже на проблему с потоками.
Гипотеза : Возможно, у вас есть основной поток и поток таймера, обращающийся к этому элементу управления. Основной поток завершает работу, вызывая Control.Dispose (), чтобы указать, что я закончил с этим элементом управления, и больше не буду вызывать этого. Однако поток таймера все еще активен - переключение контекста на этот поток, где он может вызывать методы в том же элементе управления. Теперь контроль говорит, что я Уничтожен (уже отказался от моих ресурсов), и я больше не буду работать. ObjectDisposed исключение.

Как решить эту проблему : В потоке таймера перед вызовом методов / свойств элемента управления выполните проверку с помощью

if ControlObject.IsDisposed then return; // or do whatever - but don't call control methods

ИЛИ остановите поток таймера ДО утилизации объекта.

2 голосов
/ 10 февраля 2012

У меня была та же проблема, и я решил ее, используя логический флаг, который устанавливается при закрытии формы (System.Timers.Timer не имеет свойства IsDisposed). Везде на форме я запускал таймер, он проверял этот флаг. Если он был установлен, то не запускайте таймер. Вот причина:

Причина:

Я останавливал и использовал таймер в событии закрытия формы. Я запускал таймер в событии Timer_Elapsed (). Если бы я должен был закрыть форму в середине события Timer_Elapsed (), таймер был бы немедленно уничтожен событием Form_Closing (). Это произойдет до того, как событие Timer_Elapsed () завершится, и, что более важно, до того, как оно достигнет этой строки кода:

_timer.Start()

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

Решение:

Private Sub myForm_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
    ' set the form closing flag so the timer doesn't fire even after the form is closed.
    _formIsClosing = True
    _timer.Stop()
    _timer.Dispose()
End Sub

Вот событие, прошедшее с таймера:

Private Sub Timer_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles _timer.Elapsed
    ' Don't want the timer stepping on itself (ie. the time interval elapses before the first call is done processing)
    _timer.Stop()

    ' do work here

    ' Only start the timer if the form is open. Without this check, the timer will run even if the form is closed.
    If Not _formIsClosing Then
        _timer.Interval = _refreshInterval
        _timer.Start() ' ObjectDisposedException() is thrown here unless you check the _formIsClosing flag.
    End If
End Sub

Интересно знать, что, хотя при попытке запуска таймера он вызывает исключение ObjectDisposedException, таймер все равно запускается, вызывая его запуск, даже когда форма закрыта (поток останавливается только при закрытии приложения) .

2 голосов
/ 07 сентября 2008

Остановка таймера не означает, что он не будет вызываться снова, в зависимости от того, когда вы остановите таймер, timer_tick все еще может быть поставлен в очередь в цикле сообщений для формы. То, что произойдет, это то, что вы получите еще один тик, который вы, возможно, не ожидаете. То, что вы можете сделать, находится в вашем timer_tick, проверьте свойство Enabled вашего таймера перед выполнением метода Timer_Tick.

2 голосов
/ 27 августа 2008

мы проверяем свойство IsDisposed на компонент расписания перед его использованием в событии Timer Tick, но это не так помощь.

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

Вы явно вызываете Dispose для их контроля?

1 голос
/ 13 марта 2012

Мое решение заключалось в том, чтобы поставить пробную уловку, и работает нормально

попробуй {
this.Invoke (новый EventHandler (DoUpdate)); }
catch {}

1 голос
/ 14 января 2009

Если это происходит спорадически, то я предполагаю, что это как-то связано с таймером.

Я предполагаю (и это только предположение, поскольку у меня нет доступа к вашему коду), что таймер срабатывает, пока форма закрывается. Объект dbiSchedule был удален, но таймеру все равно удается его вызвать. Это не должно происходить , потому что, если таймер имеет ссылку на объект расписания, сборщик мусора должен увидеть это и не избавляться от него.

Это заставляет меня спросить: вы вызываете Dispose () для объекта расписания вручную? Если это так, вы делаете это, прежде чем избавиться от таймера? Обязательно освободите все ссылки на объект расписания перед его удалением (т. Е. Утилизируйте таймер заранее).

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

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

1 голос
/ 27 августа 2008

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

1 голос
/ 27 августа 2008

Вы уверены, что таймер каким-то образом не переживает «dbiSchedule» и запускается после того, как «dbiSchedule» был утилизирован?

Если это так, вы могли бы воссоздать его более последовательно, если таймер сработает быстрее, что увеличит шансы на то, что вы закроете форму так же, как таймер срабатывает.

0 голосов
/ 13 апреля 2018

, поскольку папка решения находилась внутри папки OneDrive.

Если вы переместили папки с решением из одной папки на диске, ошибки исчезли.

лучший

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...