无法访问已处理的对象——如何修复?

时间:2022-07-10 16:59:41

In a VB.NET WinForms project I get an exception

在VB。NET WinForms项目我有一个例外

Cannot access a disposed 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.

Hej !谢谢你的回答。我们会停止formclosed事件上的计时器,我们会检查schedule组件上的isdispose属性,然后在Timer Tick事件中使用它,但这没有帮助。

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.

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

11 个解决方案

#1


19  

Try checking the IsDisposed property before accessing the control. You can also check it on the FormClosing event, assuming you're using the FormClosed event.

在访问控制程序之前,尝试检查已处理的属性。您还可以在FormClosed事件上检查它,假设您正在使用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.

我们会停止formclosed事件上的计时器,我们会检查schedule组件上的isdispose属性,然后在Timer Tick事件中使用它,但这没有帮助。

Calling GC.Collect before checking IsDisposed may help, but be careful with this. Read this article by Rico Mariani "When to call GC.Collect()".

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

#2


10  

Looks like a threading issue.
Hypothesis: Maybe you have a 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.

或者在处理对象之前停止计时器线程。

#3


2  

we do check the IsDisposed property on the schedule component before using it in the Timer Tick event but it doesn't help.

我们在在计时器Tick事件中使用isdispose属性之前,确实要检查schedule组件上的isdispose属性,但这没有帮助。

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 ?

#4


2  

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属性。

#5


2  

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 . timer)。Timer没有isdispose属性)。在我开始计时的地方,我让它检查这个标志。如果设置好了,那么不要启动计时器。这里是原因:

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_close()事件处理计时器。这将在Timer_Elapsed()事件结束之前发生,更重要的是,在它到达这一行代码之前:

_timer.Start()

As soon as that line was executed an ObjectDisposedException() would get thrown with the error you mentioned.

一旦执行了该行,就会抛出您提到的错误。

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

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).

有趣的是,即使它在尝试启动计时器时抛出objectdissedexception,计时器仍然会开始导致它运行,即使表单关闭(线程只在应用程序关闭时停止)。

#6


1  

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.

如果是这样的话,那么如果计时器的触发速度更快的话,您可以更一致地重新创建表单,从而增加在计时器触发时关闭表单的机会。

#7


1  

Another place you could stop the timer is the FormClosing event - this happens before the form is actually closed, so is a good place to stop things before they might access unavailable resources.

另一个可以停止计时器的地方是FormClosing事件——这发生在表单实际上关闭之前,因此在表单可能访问不可用资源之前停止事情是一个很好的地方。

#8


1  

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't happen, because if the timer has a reference to the schedule object then the garbage collector should see this and not dispose of it.

我猜测(这只是猜测,因为我无法访问您的代码)计时器正在触发,而窗体正在关闭。dbiSchedule对象已经被处理,但是计时器仍然设法尝试调用它。这不应该发生,因为如果计时器对schedule对象有引用,那么垃圾收集器应该看到这个,而不是处理它。

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).

这导致我问:您是在手动调用schedule对象上的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.

希望这个有帮助。

#9


1  

My Solution was to put a try catch, & is working fine

我的解决方案是尝试接球,并且一切正常

try {
this.Invoke(new EventHandler(DoUpdate)); }
catch { }

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

#10


0  

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()方法)时,尝试取消计时器。这看起来是最干净的解决方案。

#11


0  

because the solution folder was inside OneDrive folder.

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

If you moving the solution folders out of the one drive folder made the errors go away.

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

best

请接受我最美好的祝愿,

#1


19  

Try checking the IsDisposed property before accessing the control. You can also check it on the FormClosing event, assuming you're using the FormClosed event.

在访问控制程序之前,尝试检查已处理的属性。您还可以在FormClosed事件上检查它,假设您正在使用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.

我们会停止formclosed事件上的计时器,我们会检查schedule组件上的isdispose属性,然后在Timer Tick事件中使用它,但这没有帮助。

Calling GC.Collect before checking IsDisposed may help, but be careful with this. Read this article by Rico Mariani "When to call GC.Collect()".

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

#2


10  

Looks like a threading issue.
Hypothesis: Maybe you have a 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.

或者在处理对象之前停止计时器线程。

#3


2  

we do check the IsDisposed property on the schedule component before using it in the Timer Tick event but it doesn't help.

我们在在计时器Tick事件中使用isdispose属性之前,确实要检查schedule组件上的isdispose属性,但这没有帮助。

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 ?

#4


2  

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属性。

#5


2  

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 . timer)。Timer没有isdispose属性)。在我开始计时的地方,我让它检查这个标志。如果设置好了,那么不要启动计时器。这里是原因:

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_close()事件处理计时器。这将在Timer_Elapsed()事件结束之前发生,更重要的是,在它到达这一行代码之前:

_timer.Start()

As soon as that line was executed an ObjectDisposedException() would get thrown with the error you mentioned.

一旦执行了该行,就会抛出您提到的错误。

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

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).

有趣的是,即使它在尝试启动计时器时抛出objectdissedexception,计时器仍然会开始导致它运行,即使表单关闭(线程只在应用程序关闭时停止)。

#6


1  

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.

如果是这样的话,那么如果计时器的触发速度更快的话,您可以更一致地重新创建表单,从而增加在计时器触发时关闭表单的机会。

#7


1  

Another place you could stop the timer is the FormClosing event - this happens before the form is actually closed, so is a good place to stop things before they might access unavailable resources.

另一个可以停止计时器的地方是FormClosing事件——这发生在表单实际上关闭之前,因此在表单可能访问不可用资源之前停止事情是一个很好的地方。

#8


1  

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't happen, because if the timer has a reference to the schedule object then the garbage collector should see this and not dispose of it.

我猜测(这只是猜测,因为我无法访问您的代码)计时器正在触发,而窗体正在关闭。dbiSchedule对象已经被处理,但是计时器仍然设法尝试调用它。这不应该发生,因为如果计时器对schedule对象有引用,那么垃圾收集器应该看到这个,而不是处理它。

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).

这导致我问:您是在手动调用schedule对象上的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.

希望这个有帮助。

#9


1  

My Solution was to put a try catch, & is working fine

我的解决方案是尝试接球,并且一切正常

try {
this.Invoke(new EventHandler(DoUpdate)); }
catch { }

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

#10


0  

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()方法)时,尝试取消计时器。这看起来是最干净的解决方案。

#11


0  

because the solution folder was inside OneDrive folder.

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

If you moving the solution folders out of the one drive folder made the errors go away.

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

best

请接受我最美好的祝愿,