1

こんにちは私はwinformを使用していて、例外処理にMessageBoxを使用しようとしています。ここで奇妙なのは、MessageBoxは、メインフォーム(以下のコードでは「Form1」)が閉じられた後にのみ表示されることです。

public class Worker {
    /* edited (see below)
    public void doWork() {
        try {
            // do something
            client.Connect(serverAddress);
            stream = client.GetStream();
        }
        catch(Exception e) {
            MessageBox.Show(e.ToString(), 
                "This will not show up until Form1 is closed");
        }
    }
    */
}

public class Form1 {
    /* edited (see below)
     * public void threadProc() {
     *    Worker worker = new Worker();
     *    worker.doWork();
     * }
     */
     void button_Click(object sender, EventArgs e) {
        // create a thread that will end up throwing an exception
        Thread thread = new Thread(threadProc);
        thread.Start();
     }
}

例外処理にMessageBoxを使用するためのより良い方法は何でしょうか?


...そこで、UIスレッドにMessageBox-ingのコードをいくつか追加しましたが、問題は解決していません。

public class WorkExceptionArgs : EventArgs {
    public Exception e;
    public WorkExceptionArgs (Exception e) { this.e = e; }
}
public partial class Worker1 { // renamed (Worker->Worker1)
    /* (edited) Now Worker1 doesn't trigger any event (see below)
       public event EventHandler<WorkExceptionArgs> workException;
    */
    public void doWork() {
        try {
            // do something
            client.Connect(serverAddress);
            stream = client.GetStream();
        }
        catch(Exception e) {
            /* (edited) suppose Worker1 never throws any exception (see below)
             *  // trigger event that will cause MessageBox-ing by UI thread
             *  workException(this, new WorkExceptionArgs(e));
             */
        }
    }
}
public partial class Form1 {
    public void threadProc() {
       Worker1 worker1 = new Worker();
      /* (edited) Now Worker1 never throws any exception
       * worker.workException += new EventHandler<WorkException>(worker_WorkException);
       */
       worker1.doWork();
       // (added) After doWork() is done, Form1 creates Worker2
       Worker2 w2 = new Worker2(this, this.form2);
       w2.workException += new EventHandlerArgs<WorkExceptionArgs>(form2.worker2_WorkException);
       w2.doSomeOtherWork();
    }
    /* public void worker_WorkException(object sender, WorkExceptionArgs eArg) {
     *   MessageBox.Show(eArg.e.ToString(), "Still not showing");
     * } */
    Form2 form2 = new Form2(); // (added) At first form2 is hidden (see below)
}

実際、別のフォームと別のワーカーがありました。Worker(Worker1)がサーバーに接続すると、Form1は(.Hide())を非表示にし、Form2は(.Show())を表示し、Worker2はWorker1が作成した接続で作業を開始します。

public class Worker2 {
    Worker2(Worker1 w1, Form2 frm2) { this.w1=w1; this.frm2=frm2; }
    public Worker1 w1;
    public Form2 frm2;
    public event EventHandler<WorkExceptionArgs> workException;
    public void doSomeOtherWork() { // do some other, using data in Worker 1.
        try { // This will throw an exception
            BinaryFormatter formatter = new BinaryFormatter();
            MyObj mo = (MyObj)formatter.Deserialize(w1.getStream());
        }
        catch(Exception e) {
            workException(this, new WorkExceptionArgs(e));
        }
    }             
}

public class Form2 {
    public Form2(Form1 frm1) { // to switch from frm1 to frm2
        InitializeComponent();
        this.frm1 = frm1;
    }
    public Frm1 frm1 {get;set;}
    public void worker2_WorkException(object sender, WorkExceptionArgs ea) {
       MessageBox.Show(this, ea.e.ToString(), "SHOWS ONLY IF FORM2 IS CLOSED");
    }

}     

public partial class Form1 {
    delegate void switchWindow_Callback();
    public void switchWindow() { this.Hide(); form2.Show(); }
    public void switchWindowCb(object sender, EventArgs e) {
        if(this.InvokeRequired) {
            SwitchWindow_Callback hcb = new SwitchWindow_Callback(switchWindow);
            this.Invoke(hcb, new object[] {});
        }
        else { this.switchWindow(); }
    }
}
4

3 に答える 3

2

実際、MessageBoxはメインフォームの後ろに表示されていると思いますが、閉じるまで表示されません。

UIスレッド(作成して所有しているスレッドForm1)にMessageBox-ingを実行させる方がはるかに良いでしょう。イベントを作成するか、ワーカークラスにエラーコールバックデリゲートを設定します。

ただし、BackgroundWorker自分で作成するのではなく、ここで確認する価値があるかもしれません。致命的な例外であると仮定すると、エラー状態を保存して取得でき、スレッドが終了すると自動的に呼び出されるイベントを取得します。

于 2012-09-10T08:13:51.907 に答える
1

複数のスレッドが同時にアクセスできないように、実際にはdoWorkメソッドをロックダウンする必要があります。キューに入れる必要があります。

「スレッドの結合」を調べてください。同時に2つの例外が発生した場合を想像してみてください。アプリが倒れます。繰り返し複製されるコードの領域をロックダウンすると、スレッドが例外を処理するコードの領域にアクセスするためのキューが形成されます。

于 2012-09-10T08:23:38.673 に答える
1

lcが述べたように、メッセージボックスがメインフォームの後ろに表示されている可能性が高いため、メインフォームを閉じたときにのみ表示されます。

Windowsフォームアプリで未処理の例外を処理するために使用するモデルは、次のようになります。

// Switch-off the Windows Forms default handler for unhandled exceptions.
// NB From .NET 4 upwards, this won't work if the process state is corrupted.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

// Setup event handler to intercept an unhandled exception on a UI thread .
// NB The exception will still terminate the application. 
// But you can show a MessageBox in the event handler and log the exception. 
Application.ThreadException += 
    new ThreadExceptionEventHandler(App_UiThreadException);

// Setup event handler to intercept an unhandled exception on a non-UI thread.
AppDomain.CurrentDomain.UnhandledException += new 
    UnhandledExceptionEventHandler(App_NonUiThreadException);

// Run the application (open main form etc).

最初のは、WinFormsインフラストラクチャに処理させるのではなく、未処理の例外をキャッチして自分で処理することを示しています。.NET 4以降、この設定は、プロセス状態を破壊する例外(OutOfMemoryなど)では機能しないことに注意してください。

UIスレッドで未処理の例外がある場合、2行目で、作成した*App_UiThreadException*というプロシージャがトリガーされます。それはあなたのMesssageBoxコードが行くべきところです。

UI以外のスレッドで未処理の例外がある場合、最後の行で、作成した*App_NonUiThreadException*というプロシージャがトリガーされます。それはあなたのMesssageBoxコードが行くべきところです。

于 2012-09-10T11:21:26.203 に答える