C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ThreadException


(過去ログ 112 を表示中)

[トピック内 3 記事 (1 - 3 表示)]  << 0 >>

■66332 / inTopicNo.1)  Application.ThreadExceptionの例外
  
□投稿者/ ほくと (1回)-(2013/04/16(Tue) 11:53:25)

分類:[.NET 全般] 

いつも本サイトで勉強させてもらっています。

C#4.0にて、集約例外の実装について、検討していたのですが、
色々と実験してみたところ、例外の動作が良くわからなくなってしまいました。

Application.ThreadExceptionを使った場合、例外の動作が予想外の動作となりました。

Form1からForm2を開いて、Form2上に配置されたボタンをクリックで、例外発生させた場合、
以下のコードで、Form1側で例外をキャッチできると思っていました。

★の一行がなければ、予想通り、Form1側でキャッチすることができるのですが、
★の1行があれば、Form1でキャッチすることはできずに、「Application_ThreadException」に流れ込んでしまいます。

これって、どういう理由なのでしょうか?

Program.cs
static void Main()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); // ★
Application.Run(new Form1());
}

Form1.cs
private void button1_Click(object sender, EventArgs e)
{
try
{
var f = new Form2();
f.ShowDialog();
}
catch (ApplicationException)
{
MessageBox.Show("取れる");
}
}

Form2.cs
public Form2()
{
InitializeComponent();

}

private void button1_Click(object sender, EventArgs e)
{
throw new ApplicationException("aiueo");
}




引用返信 編集キー/
■66333 / inTopicNo.2)  Re[1]: Application.ThreadExceptionの例外
□投稿者/ pang2 (21回)-(2013/04/16(Tue) 13:32:04)
No66332 (ほくと さん) に返信
> Form1からForm2を開いて、Form2上に配置されたボタンをクリックで、例外発生させた場合、
> 以下のコードで、Form1側で例外をキャッチできると思っていました。

VSからデバッグで実行するのと、EXEを直接実行するのとで動きが違いますね。


> ★の一行がなければ、予想通り、Form1側でキャッチすることができるのですが、
> ★の1行があれば、Form1でキャッチすることはできずに、「Application_ThreadException」に流れ込んでしまいます。
>
> これって、どういう理由なのでしょうか?

Exception.StackTrace を比べてみるとか。
引用返信 編集キー/
■66345 / inTopicNo.3)  ThreadException
□投稿者/ Azulean (136回)-(2013/04/16(Tue) 22:44:39)
2013/04/16(Tue) 22:46:57 編集(投稿者)
No66332 (ほくと さん) に返信
> ★の一行がなければ、予想通り、Form1側でキャッチすることができるのですが、

それはあくまでデバッグ実行時だけです。
「デバッグなしで実行」や exe を直接起動すると、.NET の例外ダイアログが表示されることでしょう。

> ★の1行があれば、Form1でキャッチすることはできずに、「Application_ThreadException」に流れ込んでしまいます。

そういう仕様(設計)です。


ShowDialog の中ではメッセージループが回ります。
ShowDialog および Application.Run の中で実行されるメッセージループは、以下のような構造をとっています。

// 何らかのイベント発生
try
{
  // イベントの処理を実行
}
catch(Exception ex)
{
  if (/* ThreadException が設定されているか */)
  {
    // ThreadException を実行
  }else{
    // デバッグ時はデバッグハンドラに渡す
    // デバッグなし時は独自の例外ダイアログを表示する
  }
}

デバッグハンドラ周りの動きをきちんと追いかけていませんが、振る舞いから察するに、外に例外を throw するのでしょうね。
従って、ShowDialog の外の try-catch に反応します。
が、これはあくまでデバッグ時の挙動なので、この挙動に頼った設計・実装は NG です。


この挙動を変える方法として、Application.SetUnhandledExceptionMode があります。
http://msdn.microsoft.com/ja-jp/library/system.windows.forms.application.setunhandledexceptionmode.aspx

ThrowException にすると ShowDialog の外で catch できるようになりますが、ThreadException イベントは機能しなくなりますし、
ちょっとした例外でもダイアログが閉じてしまう、アプリが不正終了で例外のダイアログが出なくなってしまうといった振る舞いになることを
受容する必要があります。


ThreadException イベントを使いつつ、ShowDialog の外で catch してこっちに来るものはイベントに渡したくないというのは両立しないので、
大方針を見直してください。
(ShowDialog の外で catch するのをあきらめる or ThreadException を使うのをあきらめる)

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -