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