无法访问已释放的对象 - 如何修复?

Cannot access a disposed object - How to fix?

提问人:BTB 提问时间:8/27/2008 最后编辑:JayminBTB 更新时间:10/18/2019 访问量:273869

问:

在 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 的调度控件。表单上有一个计时器,每隔几分钟就会更新屏幕上的日程安排。

任何想法是什么导致了异常以及我如何解决它?或者甚至只是能够按需重新创建它?


呵呵!感谢您的所有回答。我们确实在 FormClosing 事件上停止了 Timer,并且在 Timer Tick 事件中使用 IsDisposed 属性之前,我们确实检查了 schedule 组件上的 IsDisposed 属性,但这无济于事。

这是一个非常烦人的问题,因为如果有人确实想出了一个有效的解决方案 - 我将无法确认解决方案,因为我无法手动重新创建问题。

.NET vb.net WinForms

评论


答:

22赞 jfs 8/27/2008 #1

在访问控件之前,请尝试检查 IsDisposed 属性。还可以在 FormClosing 事件上检查它,假设您使用的是 FormClosed 事件。

我们确实停止了 FormClosing 事件,我们确实会检查 计划中的 IsDisposed 属性 组件,然后在 Timer 中使用它 滴答事件,但它无济于事。

调用 GC.在检查 IsDisposed 之前收集可能会有所帮助,但要小心。阅读 Rico Mariani 的这篇文章“何时致电 GC。Collect()”。

1赞 imaginaryboy 8/27/2008 #2

您确定计时器不会以某种方式超过“dbiSchedule”并在“dbiSchedule”被处理后触发?

如果是这种情况,如果计时器触发速度更快,则您可能能够更一致地重新创建它,从而增加在计时器触发时关闭窗体的机会。

0赞 On Freund 8/27/2008 #3

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

11赞 Gishu 8/27/2008 #4

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

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

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

或者在释放对象之前停止计时器线程。

评论

12赞 Jesse Weigert 3/26/2009
检查 IsDisposed 将减少问题,但不能消除问题。正确的解决方案是在关闭窗体之前停止计时器。
1赞 samjudson 8/27/2008 #5

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

2赞 Will Dean 8/27/2008 #6

我们确实检查了 IsDisposed 属性 使用前的 Schedule 组件 在 Timer Tick 事件中,但事实并非如此 帮助。

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

您是否在其控件上显式调用 Dispose?

2赞 Garo Yeriazarian 9/8/2008 #7

停止计时器并不意味着不会再次调用它,具体取决于停止计时器的时间,timer_tick可能仍会在窗体的消息循环中排队。将要发生的事情是,您将再获得一个您可能意想不到的蜱虫。您可以做的是在timer_tick中,在执行 Timer_Tick 方法之前检查计时器的 Enabled 属性。

1赞 Steve J 1/15/2009 #8

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

我猜(这只是一个猜测,因为我无法访问您的代码)计时器在表单关闭时正在触发。dbiSchedule 对象已被释放,但计时器仍会以某种方式尝试调用它。这不应该发生,因为如果计时器具有对 schedule 对象的引用,则垃圾回收器应该看到这一点,而不是释放它。

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

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

希望这会有所帮助。

3赞 goku_da_master 2/11/2012 #9

我遇到了同样的问题,并使用在窗体关闭时设置的布尔标志解决了它(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赞 PUG 3/13/2012 #10

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

试试{
这个。调用(new EventHandler(DoUpdate)); }
catch { }

-1赞 Yasin Ugurlu 4/13/2018 #11

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

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

最好