在VB.NET WinForms项目中,出现异常
Cannot access a disposed of object
关闭表单时。 它很少发生,我无法按需重新创建。 堆栈跟踪如下所示:
1 2 3 4 5 6 7 8 9
| 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,并且确实在Schedule Tick事件中使用它之前检查了计划组件上的IsDisposed属性,但这无济于事。
这是一个非常烦人的问题,因为如果有人确实提出了可行的解决方案-我将无法确认解决方案,因为我无法手动重现该问题。
在访问控件之前,请尝试检查IsDisposed属性。如果您使用的是FormClosed事件,也可以在FormClosing事件中检查它。
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.
在检查IsDisposed之前调用GC.Collect可能会有所帮助,但是请谨慎使用。阅读Rico Mariani的这篇文章"何时调用GC.Collect()"。
它看起来像一个线程问题。
假设:也许您有主线程和计时器线程访问此控件。主线程关闭-调用Control.Dispose()表示我已经完成此Control,并且不再对此进行任何调用。但是,计时器线程仍处于活动状态-上下文切换到该线程,它可以在同一控件上调用方法。现在控件说我已被处分(已经放弃了我的资源),我将不再工作。 ObjectDisposed异常。
解决方法:在计时器线程中,调用控件上的方法/属性之前,请执行以下检查:
1
| if ControlObject.IsDisposed then return; // or do whatever - but don't call control methods |
或在处置对象之前停止计时器线程。
我遇到了同样的问题,并使用了一个布尔标志来解决该问题,该标志在表单关闭时被设置(System.Timers.Timer没有IsDisposed属性)。在启动计时器的表格上的任何地方,我都检查了此标志。如果已设置,则不要启动计时器。原因如下:
原因:
我在表单关闭事件中停止并处理计时器。我在Timer_Elapsed()事件中启动了计时器。如果要在Timer_Elapsed()事件的中间关闭窗体,则计时器将立即被Form_Closing()事件处理。这将发生在Timer_Elapsed()事件完成之前,更重要的是,在到达以下代码行之前:
一旦执行了该行,就会抛出ObjectDisposedException()与您提到的错误。
解决方案:
1 2 3 4 5 6
| 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 |
这是计时器经过的事件:
1 2 3 4 5 6 7 8 9 10 11 12
| 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,但即使窗体关闭,计时器仍会启动,导致其运行(线程仅在应用程序关闭时才会停止)。
停止计时器并不意味着不会再次调用它,具体取决于您何时停止计时器,timer_tick可能仍在表单的消息循环中排队。将会发生的事情是,您将得到一个您可能没有想到的刻度。您可以做的是在timer_tick中,在执行Timer_Tick方法之前检查计时器的Enabled属性。
we do check the IsDisposed property on
the schedule component before using it
in the Timer Tick event but it doesn't
help.
如果我了解该堆栈跟踪,这不是您的计时器引起的问题,而是控件本身中的一个问题-可能是他们没有正确清理。
您是否明确要求在他们的控制权上处置?
如果这种情况偶尔发生,那么我的猜测是它与计时器有关。
我正在猜测(这只是一个猜测,因为我无法访问您的代码)关闭表单时正在触发计时器。 dbiSchedule对象已被处置,但计时器仍设法设法调用它。这不应该发生,因为如果计时器具有对schedule对象的引用,则垃圾收集器应该看到此内容,而不要处理它。
这使我问:您是否在调度对象上手动调用Dispose()?如果是这样,您是在处置计时器之前这样做吗?确保在处置它之前释放对调度对象的所有引用(即,预先处置计时器)。
现在,我意识到从您发布此消息到我回答这段时间已经过去了几个月,因此希望您已经解决了此问题。我写这篇文章是为了其他可能会在以后遇到类似问题的人的利益。
希望这可以帮助。
您可以停止计时器的另一个地方是FormClosing事件-这是在窗体实际上关闭之前发生的事件,因此是在事物可能访问不可用资源之前停止事物的好地方。
您确定计时器不会以某种方式超过" dbiSchedule",并且在处理完" dbiSchedule"之后触发吗?
在这种情况下,如果计时器触发得更快,则您可能能够更一致地重新创建它,从而增加了您在计时器触发时关闭窗体的机会。
我的解决方案是尝试一下,并且工作正常
try {
this.Invoke(new EventHandler(DoUpdate));
}catch { }
查看错误堆栈跟踪,看来您的计时器仍处于活动状态。在关闭表单后尝试取消计时器(即在表单的OnClose()方法中)。这看起来是最干净的解决方案。
因为解决方案文件夹位于OneDrive文件夹内。
如果将解决方案文件夹移出一个驱动器文件夹,则错误会消失。
最好