.net 无法访问已处理的对象 - 如何解决?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Cannot access a disposed object - How to fix?
提问by BTB
In a VB.NET WinForms project, I get an exception
在 VB.NET WinForms 项目中,出现异常
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.
dbiSchedule 是来自 Dbi-tech 的日程控制。表格上有一个计时器,每隔几分钟就会更新屏幕上的时间表。
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.
嘿!感谢所有的答案。我们确实在 FormClosing 事件上停止了 Timer,并且在 Timer Tick 事件中使用它之前,我们确实检查了 schedule 组件上的 IsDisposed 属性,但它没有帮助。
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.
这是一个非常烦人的问题,因为如果有人确实提出了一个有效的解决方案 - 我将无法确认该解决方案,因为我无法手动重新创建问题。
回答by jfs
Try checking the IsDisposedproperty before accessing the control. You can also check it on the FormClosingevent, assuming you're using the FormClosed event.
在访问控件之前尝试检查IsDisposed属性。您还可以在FormClosing事件上检查它,假设您正在使用 FormClosed 事件。
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.
我们确实在 FormClosing 事件上停止了 Timer,并且在 Timer Tick 事件中使用它之前,我们确实检查了 schedule 组件上的 IsDisposed 属性,但它没有帮助。
Calling GC.Collect before checking IsDisposed may help, but be careful with this. Read this article by Rico Mariani "When to call GC.Collect()".
在检查 IsDisposed 之前调用 GC.Collect 可能会有所帮助,但要小心。阅读 Rico Mariani 的这篇文章“何时调用 GC.Collect()”。
回答by Gishu
It looks like a threading issue.
Hypothesis: Maybe you have the main thread and a timer thread accessing this control. The main thread shuts down - calling Control.Dispose() to indicate that I'm done with this Control and I shall make no more calls to this. However, the timer thread is still active - a context switch to that thread, where it may call methods on the same control. Now the control says I'm Disposed (already given up my resources) and I shall not work anymore. ObjectDisposed exception.
看起来像是线程问题。
假设:也许您有访问此控件的主线程和计时器线程。主线程关闭 - 调用 Control.Dispose() 以指示我已完成此控件,我将不再调用此控件。但是,计时器线程仍然处于活动状态——上下文切换到该线程,在那里它可以调用同一控件上的方法。现在控件说我被处置了(已经放弃了我的资源),我将不再工作。ObjectDisposed 异常。
How to solve this: In the timer thread, before calling methods/properties on the control, do a check with
如何解决这个问题:在定时器线程中,在调用控件上的方法/属性之前,先检查一下
if ControlObject.IsDisposed then return; // or do whatever - but don't call control methods
OR stop the timer thread BEFORE disposing the object.
或在处置对象之前停止计时器线程。
回答by Will Dean
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 事件中使用它之前,我们确实检查了 schedule 组件上的 IsDisposed 属性,但它没有帮助。
If I understand that stack trace, it's not your timer which is the problem, it's one in the control itself - it might be them who are not cleaning-up properly.
如果我理解堆栈跟踪,那么问题不是您的计时器,而是控件本身的一个 - 可能是他们没有正确清理。
Are you explicitly calling Dispose on their control?
您是否在他们的控件上显式调用 Dispose?
回答by Garo Yeriazarian
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.
停止计时器并不意味着它不会再次被调用,这取决于您何时停止计时器,timer_tick 可能仍会在表单的消息循环中排队。将会发生的情况是,您会再得到一个您可能没有预料到的滴答声。您可以做的是在 timer_tick 中,在执行 Timer_Tick 方法之前检查计时器的 Enabled 属性。
回答by goku_da_master
I had the same problem and solved it using a boolean flag that gets set when the form is closing (the System.Timers.Timer does not have an IsDisposed property). Everywhere on the form I was starting the timer, I had it check this flag. If it was set, then don't start the timer. Here's the reason:
我遇到了同样的问题,并使用在表单关闭时设置的布尔标志解决了它(System.Timers.Timer 没有 IsDisposed 属性)。在我启动计时器的表单上的每个地方,我都让它检查这个标志。如果已设置,则不要启动计时器。原因如下:
The Reason:
原因:
I was stopping and disposing of the timer in the form closing event. I was starting the timer in the Timer_Elapsed() event. If I were to close the form in the middle of the Timer_Elapsed() event, the timer would immediately get disposed by the Form_Closing() event. This would happen before the Timer_Elapsed() event would finish and more importantly, before it got to this line of code:
我在表单关闭事件中停止并处理计时器。我在 Timer_Elapsed() 事件中启动计时器。如果我在 Timer_Elapsed() 事件中间关闭表单,则计时器将立即被 Form_Closing() 事件处理。这将在 Timer_Elapsed() 事件完成之前发生,更重要的是,在它到达这行代码之前:
_timer.Start()
As soon as that line was executed an ObjectDisposedException() would get thrown with the error you mentioned.
一旦执行该行,就会抛出 ObjectDisposedException() 并显示您提到的错误。
The Solution:
解决方案:
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
Here's the timer elapsed event:
这是计时器已用事件:
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
The interesting thing to know, even though it would throw the ObjectDisposedException when attempting to start the timer, the timer would still get started causing it to run even when the form was closed (the thread would only stop when the application was closed).
有趣的是,即使在尝试启动计时器时它会抛出 ObjectDisposedException,计时器仍然会启动,从而即使在窗体关闭时它也会运行(线程只会在应用程序关闭时停止)。
回答by Steve J
If this happens sporadically then my guess is that it has something to do with the timer.
如果这种情况偶尔发生,那么我的猜测是它与计时器有关。
I'm guessing (and this is only a guess since I have no access to your code) that the timer is firing while the form is being closed. The dbiSchedule object has been disposed but the timer somehow still manages to try to call it. This shouldn'thappen, because if the timer has a reference to the schedule object then the garbage collector should see this and not dispose of it.
我猜测(这只是一个猜测,因为我无法访问您的代码)计时器正在关闭表单时触发。dbiSchedule 对象已被处理,但计时器仍然设法尝试调用它。这不应该发生,因为如果计时器引用了调度对象,那么垃圾收集器应该看到它而不是处理它。
This leads me to ask: are you calling Dispose() on the schedule object manually? If so, are you doing that before disposing of the timer? Be sure that you release all references to the schedule object before Disposing it (i.e. dispose of the timer beforehand).
这让我问:你是手动调用调度对象上的 Dispose() 吗?如果是这样,您是否在处理计时器之前这样做?确保在处置之前释放对调度对象的所有引用(即预先处置计时器)。
Now I realize that a few months have passed between the time you posted this and when I am answering, so hopefully, you have resolved this issue. I'm writing this for the benefit of others who may come along later with a similar issue.
现在我意识到从你发布这篇文章到我回答这个问题已经过去了几个月,所以希望你已经解决了这个问题。我写这篇文章是为了其他人可能会遇到类似的问题。
Hope this helps.
希望这可以帮助。
回答by samjudson
Another place you could stop the timer is the FormClosingevent - this happens before the form is actually closed, so is a good place to stop things before they might access unavailable resources.
另一个可以停止计时器的地方是FormClosing事件 - 这发生在表单实际关闭之前,因此是在它们可能访问不可用资源之前停止事情的好地方。
回答by imaginaryboy
You sure the timer isn't outliving the 'dbiSchedule' somehow and firing after the 'dbiSchedule' has been been disposed of?
您确定计时器不会以某种方式超过“dbiSchedule”并在“dbiSchedule”被处理后触发?
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.
如果是这种情况,如果计时器触发得更快,您可能能够更一致地重新创建它,从而增加您在计时器触发时关闭表单的机会。
回答by On Freund
Looking at the error stack trace, it seems your timer is still active. Try to cancel the timer upon closing the form (i.e.in the form's OnClose() method). This looks like the cleanest solution.
查看错误堆栈跟踪,您的计时器似乎仍处于活动状态。尝试在关闭表单时取消计时器(即在表单的 OnClose() 方法中)。这看起来是最干净的解决方案。
回答by PUG
My Solution was to put a try catch, & is working fine
我的解决方案是尝试捕获,并且工作正常
try {
this.Invoke(new EventHandler(DoUpdate)); }
catch { }
试试 {
this.Invoke(new EventHandler(DoUpdate)); }
抓住{}

