提问人:BTB 提问时间:8/27/2008 最后编辑:JayminBTB 更新时间:10/18/2019 访问量:273869
无法访问已释放的对象 - 如何修复?
Cannot access a disposed object - How to fix?
问:
在 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 属性,但这无济于事。
这是一个非常烦人的问题,因为如果有人确实想出了一个有效的解决方案 - 我将无法确认解决方案,因为我无法手动重新创建问题。
答:
在访问控件之前,请尝试检查 IsDisposed 属性。还可以在 FormClosing 事件上检查它,假设您使用的是 FormClosed 事件。
我们确实停止了 FormClosing 事件,我们确实会检查 计划中的 IsDisposed 属性 组件,然后在 Timer 中使用它 滴答事件,但它无济于事。
调用 GC.在检查 IsDisposed 之前收集可能会有所帮助,但要小心。阅读 Rico Mariani 的这篇文章“何时致电 GC。Collect()”。
您确定计时器不会以某种方式超过“dbiSchedule”并在“dbiSchedule”被处理后触发?
如果是这种情况,如果计时器触发速度更快,则您可能能够更一致地重新创建它,从而增加在计时器触发时关闭窗体的机会。
查看错误堆栈跟踪,您的计时器似乎仍处于活动状态。尝试在关闭表单时取消计时器(即在表单的 OnClose() 方法中)。这看起来是最干净的解决方案。
这看起来像一个线程问题。
假设:也许你有主线程和一个计时器线程访问此控件。主线程关闭 - 调用 Control.Dispose() 以指示我已完成此控件,我将不再对此调用。但是,计时器线程仍处于活动状态 - 上下文切换到该线程,它可以在同一控件上调用方法。现在控件说我被处置了(已经放弃了我的资源),我将不再工作了。ObjectDisposed 异常。
如何解决此问题:在计时器线程中,在调用控件上的方法/属性之前,请检查
if ControlObject.IsDisposed then return; // or do whatever - but don't call control methods
或者在释放对象之前停止计时器线程。
评论
另一个可以停止计时器的位置是 FormClosing 事件 - 这发生在窗体实际关闭之前,因此是在它们可能访问不可用资源之前停止操作的好地方。
我们确实检查了 IsDisposed 属性 使用前的 Schedule 组件 在 Timer Tick 事件中,但事实并非如此 帮助。
如果我理解堆栈跟踪,那么问题不在于您的计时器,而在于控件本身的问题 - 可能是他们没有正确清理。
您是否在其控件上显式调用 Dispose?
停止计时器并不意味着不会再次调用它,具体取决于停止计时器的时间,timer_tick可能仍会在窗体的消息循环中排队。将要发生的事情是,您将再获得一个您可能意想不到的蜱虫。您可以做的是在timer_tick中,在执行 Timer_Tick 方法之前检查计时器的 Enabled 属性。
如果这种情况偶尔发生,那么我的猜测是它与计时器有关。
我猜(这只是一个猜测,因为我无法访问您的代码)计时器在表单关闭时正在触发。dbiSchedule 对象已被释放,但计时器仍会以某种方式尝试调用它。这不应该发生,因为如果计时器具有对 schedule 对象的引用,则垃圾回收器应该看到这一点,而不是释放它。
这让我问:您是否手动在计划对象上调用 Dispose()?如果是这样,您是否在处理计时器之前这样做?请确保在释放 schedule 对象之前释放对它的所有引用(即事先释放计时器)。
现在我意识到,从您发布此内容到我回答已经过去了几个月,所以希望您已经解决了这个问题。我写这篇文章是为了其他人的利益,他们以后可能会遇到类似的问题。
希望这会有所帮助。
我遇到了同样的问题,并使用在窗体关闭时设置的布尔标志解决了它(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,计时器仍然会启动,导致它即使在窗体关闭时也会运行(线程只会在应用程序关闭时停止)。
我的解决方案是尝试一下,并且工作正常
试试{
这个。调用(new EventHandler(DoUpdate)); }
catch { }
因为解决方案文件夹位于 OneDrive 文件夹中。
如果将解决方案文件夹移出一个驱动器文件夹,则错误会消失。
最好
评论