4

まだ破棄されていない FormObjectDisposedExceptionの呼び出しからを取得します。Invoke問題を示すサンプル コードを次に示します。

public partial class Form2 : Form
{
    void Form2_Load(object sender, EventArgs e)
    {
        // Start a task that does an Invoke on this control
        Task.Factory.StartNew(TaskWork); 

        // Sleep here long enough to allow the task that does the Invoke 
        // to execute to the point where it has:
        // a. Posted the message and 
        // b. is waiting 
        Thread.Sleep(500);

        // Cause ShowDialog to return by setting the DialogResult
        DialogResult = DialogResult.OK;
    }

    void TaskWork()
    {
        // This call doesn't return, but instead throws an ObjectDisposedException
        this.Invoke((MethodInvoker)(() => MessageBox.Show("Invoke succeeded")));
    }
}

Form1 (メイン フォーム) からの呼び出しコードは次のとおりです。

public partial class Form1 : Form
{
    Form2 m_form2 = new Form2();

    void Form1_Load(object sender, EventArgs e)
    {
        // Call ShowDialog, but don't dispose it.
        m_form2.ShowDialog();

        // Cause the finalizers to run.  This causes an AggregateException to be thrown
        // due to the unhandled ObjectDisposedException from the Task.
        GC.Collect(); 
    }
}

Microsoft のソースを調べたところ、DestroyHandle の呼び出し中に例外が作成されることがわかりました (下記)。DestroyHandle は、終了時に ShowDialog によって呼び出されています。

source.NET\4\DEVDIV_TFS\Dev10\Releases\RTMRel\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs\1305376\Control.cs から:

protected virtual void DestroyHandle() {
    // ...
        // If we're not recreating the handle, then any items in the thread callback list will
        // be orphaned.  An orphaned item is bad, because it will cause the thread to never 
        // wake up.  So, we put exceptions into all these items and wake up all threads. 
        // If we are recreating the handle, then we're fine because recreation will re-post
        // the thread callback message to the new handle for us. 
        //
        if (!RecreatingHandle) {
            if (threadCallbackList != null) {
                lock (threadCallbackList) { 
                    Exception ex = new System.ObjectDisposedException(GetType().Name);

                    while (threadCallbackList.Count > 0) { 
                        ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue();
                        entry.exception = ex; 
                        entry.Complete();
                    }
                }
            } 
        }
    // ...
}    

質問:

  1. ShowDialog がハンドルを破棄するのはなぜですか (このフォームを再利用する可能性がある場合)。

  2. ObjectDisposedException が発生するのはなぜですか。これは非常に誤解を招くものです (破棄されていないため)。オブジェクトが破棄されたときにのみハンドルが破棄されることをコードが期待しているかのようです(これは私が期待していたものです)。

  3. これは有効であるべきですか?つまり、ShowDialog の後にコントロールを呼び出すことを許可する必要がありますか?

ノート:

  1. もう一度実行する.ShowDialog()と、新しいハンドルが作成されます。

  2. 実行後に を実行.ShowDialog()しようとするInvokeと、「ウィンドウ ハンドルが作成されるまで、Invoke または BeginInvoke をコントロールで呼び出すことはできません」という InvalidOperationException が発生します。

  3. 実行後、プロパティ.ShowDialog()にアクセスして を実行すると、成功します。HandleInvoke

4

1 に答える 1

4

ShowDialogがハンドルを破壊するのはなぜですか(このフォームを再利用する可能性がある場合)?

ネイティブウィンドウを破棄して非表示にする。この後、HandleプロパティはIntPtr.Zeroになります。

ObjectDisposedExceptionが発生するのはなぜですか?非常に誤解を招く可能性があります(破棄されていないため)。

はい、ダイアログの場合は誤解を招きます。このコードは、Show()で表示されるフォームのより一般的なケースを処理するために作成されました。ネイティブウィンドウが破棄されると、保留中の呼び出しのキューを下に移動し、それらに完了のマークを付けます。そして、「最後に発生した例外」ステータスをObjectDisposedException(参照ソーススニペットのentry.exception)に設定します。これは、呼び出されたコードがネイティブウィンドウがなくなった状態で実行されないようにするために明示的に実行されます。このようなコードは、通常、ODEで停止します。それはただ銃をジャンプさせ、例外を早期に発生させます。InvalidOperationExceptionの方が適切であると主張することもできますが、彼らはODEを選択しました。

これは有効である必要がありますか?つまり、ShowDialogの後にコントロールを呼び出すことを許可する必要がありますか?

いいえ、それは機能しません。Handleプロパティの値は、呼び出すスレッドを把握するために必要です。ただし、ShowDialog()が戻った後はIntPtr.Zeroです。

ここで問題が発生しますが、一般的な戦略では、フォームを閉じる前に、スレッドが完了または終了していることを確認する必要があります。この回答でそれについてもっと詳しく。

于 2012-09-14T18:15:27.543 に答える