Не удается получить доступ к удаленному объекту - как исправить?

В проекте 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, но это не помогает.

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Вы явно вызываете Dispose на свой контроль?

Ответ 4

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

Ответ 5

У меня была та же проблема, и я решил ее с помощью логического флага, который устанавливается при закрытии формы (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 does not 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, таймер все равно запускается, вызывая его запуск даже при закрытии формы (поток останавливается только при закрытии приложения).

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

Ответ 9

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

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

Ответ 10

Рассматривая трассировку стека ошибок, кажется, что ваш таймер все еще активен. Попытайтесь отменить таймер после закрытия формы (т.е. В методе OnClose()). Это выглядит как самое чистое решение.

Ответ 11

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

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

Лучший