38

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 Tick イベントで使用する前にスケジュール コンポーネントの IsDisposed プロパティをチェックしますが、役に立ちません。

誰かがうまくいく解決策を思いついたとしても、手動で問題を再現できないため、解決策を確認できないため、これは本当に厄介な問題です。

4

11 に答える 11

21

コントロールにアクセスする前に、 IsDisposedプロパティを確認してください。FormClosed イベントを使用していると仮定すると、 FormClosingイベントでも確認できます。

FormClosing イベントでタイマーを停止し、Timer Tick イベントで使用する前にスケジュール コンポーネントの IsDisposed プロパティをチェックしますが、役に立ちません。

IsDisposed を確認する前に GC.Collect を呼び出すと役立つ場合がありますが、これには注意してください。Rico Mariani によるこの記事「いつ GC.Collect() を呼び出すか」をお読みください。

于 2008-08-27T06:45:36.173 に答える
10

スレッドの問題のようです。
仮説: メイン スレッドとタイマー スレッドがこのコントロールにアクセスしている可能性があります。メイン スレッドがシャットダウンします。Control.Dispose() を呼び出して、この Control の処理が完了したことを示し、これ以上呼び出しを行わないことを示します。ただし、タイマー スレッドはまだアクティブです。つまり、そのスレッドへのコンテキスト スイッチであり、同じコントロールのメソッドを呼び出すことができます。これで、コントロールは私が処分された (すでに私のリソースをあきらめている) と言い、私はもう仕事をしません。ObjectDisposed 例外。

これを解決する方法: タイマー スレッドで、コントロールのメソッド/プロパティを呼び出す前に、次のチェックを行います。

if ControlObject.IsDisposed then return; // or do whatever - but don't call control methods

または、オブジェクトを破棄する前にタイマー スレッドを停止します。

于 2008-08-27T06:49:16.133 に答える
2

私は同じ問題を抱えていて、フォームが閉じているときに設定されるブール値フラグを使用して解決しました (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 がスローされますが、タイマーは開始され、フォームが閉じられても実行されます (スレッドはアプリケーションが閉じられたときにのみ停止します)。

于 2012-02-10T18:14:28.947 に答える
2

Timer Tick イベントで使用する前にスケジュール コンポーネントの IsDisposed プロパティを確認しますが、役に立ちません。

そのスタック トレースを理解している場合、問題はタイマーではなく、コントロール自体にあるものです。適切にクリーンアップされていない可能性があります。

コントロールで Dispose を明示的に呼び出していますか?

于 2008-08-27T08:41:42.493 に答える
2

タイマーを停止しても、再度呼び出されないというわけではありません。タイマーを停止するタイミングによっては、timer_tick がフォームのメッセージ ループでキューに入れられたままになる場合があります。何が起こるかというと、予想していなかったかもしれないもう 1 つのティックを得るということです。できることは、timer_tick で、Timer_Tick メソッドを実行する前にタイマーの Enabled プロパティを確認することです。

于 2008-09-07T17:32:52.110 に答える
1

タイマーが何らかの形で「dbiSchedule」よりも長生きしておらず、「dbiSchedule」が破棄された後に起動していないことを確認していますか?

その場合、タイマーがより速く起動すると、より一貫して再作成できる可能性があるため、タイマーが起動しているときにフォームを閉じる可能性が高くなります。

于 2008-08-27T06:47:00.130 に答える
1

タイマーを停止できる別の場所はFormClosingイベントです。これは、フォームが実際に閉じられる前に発生するため、使用できないリソースにアクセスする前に停止するのに適した場所です。

于 2008-08-27T07:01:49.627 に答える
1

これが散発的に発生する場合は、タイマーと関係があると思います。

フォームが閉じられている間にタイマーが起動していると推測しています(コードにアクセスできないため、これは推測にすぎません)。dbiSchedule オブジェクトは破棄されましたが、タイマーは何とかそれを呼び出そうとしています。タイマーがスケジュール オブジェクトへの参照を持っている場合、ガベージ コレクターはこれを確認し、破棄しないため、これは発生しないはずです

これにより、私は質問するようになります: スケジュール オブジェクトで手動で Dispose() を呼び出していますか? もしそうなら、タイマーを処分する前にそれをしていますか?スケジュール オブジェクトを破棄する前に、スケジュール オブジェクトへのすべての参照を必ず解放してください (つまり、事前にタイマーを破棄します)。

これを投稿してから返信するまでに数か月が経過したことに気づきました。この問題が解決したことを願っています。後で同様の問題に遭遇する可能性のある他の人の利益のためにこれを書いています。

お役に立てれば。

于 2009-01-14T17:58:13.043 に答える
0

私の解決策は、try catch を配置することであり、正常に動作しています

{
this.Invoke(new EventHandler(DoUpdate)); を試してください。}
キャッチ { }

于 2012-03-13T13:21:05.247 に答える
0

エラー スタック トレースを見ると、タイマーがまだアクティブになっているようです。フォームを閉じる際に (つまり、フォームの OnClose() メソッドで)タイマーをキャンセルしてみてください。これは最もクリーンなソリューションのように見えます。

于 2008-08-27T06:48:14.527 に答える