38

In a VB.NET WinForms project, I get an exception

Cannot access a disposed of object

when closing a form. It occurs very rarely and I cannot recreate it on demand. The stack trace looks like this:

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)

The dbiSchedule is a schedule control from Dbi-tech. There is a timer on the form that updates the schedule on the screen every few minutes.

Any ideas what is causing the exception and how I might go about fixing it? or even just being able to recreate it on demand?


Hej! Thanks for all the answers. We do stop the Timer on the FormClosing event and we do check the IsDisposed property on the schedule component before using it in the Timer Tick event but it doesn't help.

It's a really annoying problem because if someone did come up with a solution that worked - I wouldn't be able to confirm the solution because I cannot recreate the problem manually.

4

11 回答 11

21

Try checking the IsDisposed property before accessing the control. You can also check it on the FormClosing event, assuming you're using the FormClosed event.

We do stop the Timer on the FormClosing event and we do check the IsDisposed property on the schedule component before using it in the Timer Tick event but it doesn't help.

Calling GC.Collect before checking IsDisposed may help, but be careful with this. Read this article by Rico Mariani "When to call GC.Collect()".

于 2008-08-27T06:45:36.173 回答
10

它看起来像一个线程问题。
假设:也许你有主线程和一个定时器线程访问这个控件。主线程关闭 - 调用 Control.Dispose() 表示我已完成此 Control,我将不再对此进行调用。但是,计时器线程仍然处于活动状态 - 上下文切换到该线程,它可以在同一控件上调用方法。现在控制说我被处置了(已经放弃了我的资源),我将不再工作。ObjectDisposed 异常。

如何解决:在计时器线程中,在调用控件上的方法/属性之前,先检查一下

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

或在处置对象之前停止计时器线程。

于 2008-08-27T06:49:16.133 回答
2

在 Timer Tick 事件中使用它之前,我们确实检查了 schedule 组件上的 IsDisposed 属性,但它没有帮助。

如果我理解该堆栈跟踪,那么问题不是您的计时器,而是控件本身中的一个 - 可能是他们没有正确清理。

您是否在他们的控制下明确调用 Dispose ?

于 2008-08-27T08:41:42.493 回答
2

Stopping the timer doesn't mean that it won't be called again, depending on when you stop the timer, the timer_tick may still be queued on the message loop for the form. What will happen is that you'll get one more tick that you may not be expecting. What you can do is in your timer_tick, check the Enabled property of your timer before executing the Timer_Tick method.

于 2008-09-07T17:32:52.110 回答
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,但即使关闭表单,计时器仍会启动,导致它运行(线程只会在应用程序关闭时停止)。

于 2012-02-10T18:14:28.947 回答
1

如果这种情况偶尔发生,那么我的猜测是它与计时器有关。

我猜测(这只是一个猜测,因为我无法访问您的代码)在表单关闭时计时器正在触发。dbiSchedule 对象已被释放,但计时器仍然设法尝试调用它。这不应该发生,因为如果计时器引用了调度对象,那么垃圾收集器应该看到它而不是处理它。

这让我问:你是在调度对象上手动调用 Dispose() 吗?如果是这样,您是否在处理计时器之前这样做?确保在 Dispose 之前释放对 schedule 对象的所有引用(即提前释放计时器)。

现在我意识到从你发布这个到我回答之间已经过去了几个月,所以希望你已经解决了这个问题。我写这篇文章是为了其他可能会遇到类似问题的人。

希望这可以帮助。

于 2009-01-14T17:58:13.043 回答
1

You sure the timer isn't outliving the 'dbiSchedule' somehow and firing after the 'dbiSchedule' has been been disposed of?

If that is the case you might be able to recreate it more consistently if the timer fires more quickly thus increasing the chances of you closing the Form just as the timer is firing.

于 2008-08-27T06:47:00.130 回答
1

您可以停止计时器的另一个地方是FormClosing事件 - 这发生在表单实际关闭之前,因此是在它们可能访问不可用资源之前停止事物的好地方。

于 2008-08-27T07:01:49.627 回答
0

查看错误堆栈跟踪,您的计时器似乎仍然处于活动状态。尝试在关闭表单时取消计时器(在表单的 OnClose() 方法中)。这看起来是最干净的解决方案。

于 2008-08-27T06:48:14.527 回答
0

我的解决方案是尝试捕获,并且工作正常

尝试 {
this.Invoke(new EventHandler(DoUpdate)); }
抓住 { }

于 2012-03-13T13:21:05.247 回答
-1

因为解决方案文件夹位于 OneDrive 文件夹中。

如果您将解决方案文件夹从一个驱动器文件夹中移出,则错误会消失。

最好的

于 2018-04-13T12:30:22.533 回答