2008-08-27 4 views
28

В проекте 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. В форме есть таймер, который каждые несколько минут обновляет расписание на экране.

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


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

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

ответ

19

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

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

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

1

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

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

0

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

10

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

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

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

или остановить поток таймера перед удалением объекта.

+11

Проверка на IsDisposed уменьшит, но не устранит проблему. Правильное решение - остановить таймер перед закрытием формы. – 2009-03-26 09:55:19

1

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

2

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

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

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

2

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

1

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

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

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

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

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

2

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

0

Мое решение было положить попробовать поймать, & работает отлично

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

Смежные вопросы