質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio 2010

Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

Q&A

解決済

3回答

3270閲覧

Winformsで例外を一括処理したい

shun_kuwa

総合スコア187

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio 2010

Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

0グッド

0クリップ

投稿2018/10/30 02:30

編集2018/10/30 04:56

実現したいこと

winformsで実装されたデスクトップアプリケーション内にて、発生した全ての例外をまとめて拾いたいです。
もちろん個々の機能でも例外処理を実装していますが、想定できていない部分で例外が発生したときに、ユーザに分かりやすいメッセージを表示したいと悩んでいます。
現状は「アプリケーションのコンポーネントで、ハンドルされていない例外が~」のダイアログが表示されてしまうので、使用者にとって分かりにくい(どうすればいいか分からない)状態となっております。

試したこと

以下のサイトを参考に、Program.csにてApplication.ThreadExceptionThread.GetDomain().UnhandledExceptionを拾うようにソースを書き換えてみました。

適切に処理されなかった例外をキャッチするには?

C#

1using System; 2using System.Windows.Forms; 3using System.Threading; 4 5namespace WindowsApplication1 6{ 7 public class Program 8 { 9 [STAThread] 10 static void Main() 11 { 12 // ThreadExceptionイベント・ハンドラを登録する 13 Application.ThreadException += new 14 ThreadExceptionEventHandler(Application_ThreadException); 15 16 // UnhandledExceptionイベント・ハンドラを登録する 17 Thread.GetDomain().UnhandledException += new 18 UnhandledExceptionEventHandler(Application_UnhandledException); 19 20 // メイン・スレッド以外の例外はUnhandledExceptionでハンドル 21 //string buffer = "1"; char error = buffer[2]; 22 23 // ここで実行されるメイン・アプリケーション・スレッドの例外は 24 // Application_ThreadExceptionでハンドルできる 25 Application.Run(new Form1()); 26 } 27 28 // 未処理例外をキャッチするイベント・ハンドラ 29 // (Windowsアプリケーション用) 30 public static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 31 { 32 ShowErrorMessage(e.Exception, "Application_ThreadExceptionによる例外通知です。"); 33 } 34 35 // 未処理例外をキャッチするイベント・ハンドラ 36 // (主にコンソール・アプリケーション用) 37 public static void Application_UnhandledException(object sender, UnhandledExceptionEventArgs e) 38 { 39 Exception ex = e.ExceptionObject as Exception; 40 if (ex != null) 41 { 42 ShowErrorMessage(ex, "Application_UnhandledExceptionによる例外通知です。"); 43 } 44 } 45 46 // ユーザー・フレンドリなダイアログを表示するメソッド 47 public static void ShowErrorMessage(Exception ex, string extraMessage) 48 { 49 MessageBox.Show(extraMessage + " \n――――――――\n\n" + 50 "エラーが発生しました。開発元にお知らせください\n\n" + 51 "【エラー内容】\n" + ex.Message + "\n\n" + 52 "【スタックトレース】\n" + ex.StackTrace); 53 } 54 } 55} 56

発生している問題

上記のソースコードを追加したことで、Visual Studio上でのデバッグ実行(Release構成)からは想定通りのメッセージを出力できました。
しかし、同じビルドで出力された.exeファイルをエクスプローラから直接実行すると、もともとの「アプリケーションのコンポーネントで、ハンドルされていない例外が~」のダイアログが出力されてしまいます。
他の環境で試しても同じ結果でした。

環境など

  • Windows 10
  • IDE:Visual Studio 2010
  • プロジェクトのターゲットプラットフォーム:.Net Framework4.0

該当のプログラムでは、起動後にいくつか別のスレッドを起動(Threadインスタンスを生成し、Start()させている)しているのでその辺りが関係しているのかなと思っています。
同じ環境で、スレッドを起動しないプログラムを作ってみたところ、デバッグ実行でも.exeの直接実行でも同じように想定通りの動きをしていました。

情報に不足があれば追加しますのでご指摘ください。どうかよろしくお願いいたします。

実際に問題が発生するコード

以下に実際の問題(自前のMessageBoxが出力したいのに、Windowsの「ハンドルされていない例外が~」が表示されてしまう。)が発生しているソースの簡易版を載せます。
上記の通りですが、Visual Studio上のデバッグでは想定通りの動きをしており、exeを直接実行した場合に問題が発生します。

C#

1using System; 2using System.Threading; 3using System.Windows.Forms; 4 5namespace SandboxForms 6{ 7 static class Program 8 { 9 /// <summary> 10 /// The main entry point for the application. 11 /// </summary> 12 [STAThread] 13 static void Main() 14 { 15 Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); 16 System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(Program_UnhandledException); 17 18 Application.EnableVisualStyles(); 19 Application.SetCompatibleTextRenderingDefault(false); 20 Application.Run(new Form1()); 21 } 22 23 static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 24 { 25 var errorMember = e.Exception.TargetSite; 26 var errorMessage = e.Exception.Message; 27 var message = string.Format("異常が発生しました。システム管理者へ連絡してください。\r\n例外情報:例外が{0}で発生。プログラムを終了します。\r\nメッセージ:{1}", 28 errorMember, errorMessage); 29 MessageBox.Show(message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Stop); 30 } 31 32 static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e) 33 { 34 var ex = e.ExceptionObject as Exception; 35 if (ex == null) 36 { 37 MessageBox.Show("異常が発生しました。システム管理者へ連絡してください。\r\n例外情報なし"); 38 } 39 40 var errorMember = ex.TargetSite.Name; 41 var errorMessage = ex.Message; 42 var message = string.Format("異常が発生しました。システム管理者へ連絡してください。\r\n例外情報:例外が{0}で発生。プログラムを終了します。\r\nメッセージ:{1}", 43 errorMember, errorMessage); 44 MessageBox.Show(message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Stop); 45 } 46 } 47} 48 49-------------------------------------------------------------------------- 50 51using System; 52using System.Threading; 53using System.Windows.Forms; 54 55namespace SandboxForms 56{ 57 public partial class Form1 : Form 58 { 59 public Form1() 60 { 61 InitializeComponent(); 62 } 63 private void button1_Click(object sender, EventArgs e) 64 { 65 throw new ApplicationException("例外のテスト。"); 66 } 67 68 private void Form1_Load(object sender, EventArgs e) 69 { 70 ThreadStart ts = new ThreadStart(Form2Open); 71 Thread t = new Thread(ts); 72 t.Name = "test"; 73 t.SetApartmentState(ApartmentState.STA); 74 t.Start(); 75 } 76 77 private void Form2Open() 78 { 79 var form = new Form2(); 80 form.ShowDialog(); 81 } 82 } 83} 84 85-------------------------------------------------------------------------- 86 87using System; 88using System.Windows.Forms; 89 90namespace SandboxForms 91{ 92 public partial class Form2 : Form 93 { 94 public Form2() 95 { 96 InitializeComponent(); 97 } 98 99 private void button1_Click(object sender, EventArgs e) 100 { 101 throw new Exception("Form2の例外"); 102 } 103 } 104} 105

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

よくやる手として、詳細なエラーはエラーファイル(textファイル)なんかで年月日が含まれるファイル名
で吐き出すようにしておき、ユーザーに知らせるメッセージとして「論理エラーが発生しましたシステムエンジニアに問合せください」みたいなメッセージを表示する。解析ファイルには、エラーファイルだけでなく、ログファイル(どんな操作を行ったかとか、どこのロジックを通過したかみたいな記録)なんかも用意することもあります。(量が少ない場合には一緒のファイルでもいい)
SEはこの連絡がきたら、所定のファイルを送信してもらって、解析する。みたいなことします。
もうひとつの手として、フォームに細かく表示させたい場合には、メッセージボックスで出すのではなく、フォーム自体にメッセージ欄(たとえば、フォーム最下部に共通フォームとして用意しておく)そこに、複数行のエラーメッセージを表示できるようにし、利用者はそのメッセージ欄をスクロールして、一括でメッセージの内容を確認できるようにする。というような設計思想を取り入れます。あるいは、そのメッセージを印刷できるようにしておく。私が経験したのはそれぐらいです。

投稿2018/10/30 05:34

akirafudo6

総合スコア341

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shun_kuwa

2018/10/30 06:03

ご回答ありがとうございます。 色々なやり方があるとは思いますが、そのためにもまずは「例外を漏れなくハンドルしたい」というのが今回のご質問の趣旨となっております。 その後のエラーの扱い方については非常に勉強になりました。ありがとうございます。
guest

0

ベストアンサー

確認してみましたが、うちでは作られたダイアログがちゃんと出ますので、その部分のコードに問題はありません。

新しくボタンを貼り付けてビルドし、エクスプローラーから実行してみてください。
その際、ボタンが表示されなければ、ビルドされたものとは別のものを実行しています。

投稿2018/10/30 03:27

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shun_kuwa

2018/10/30 04:24

回答ありがとうございます。 ボタンを貼り付けたものが、エクスプローラーからも起動されましたのでビルドされたものは間違いないと思います。 また質問下部に少し記載させていただきましたが、今触っているプログラムでは起動後に別のスレッドを起動しています。 質問内に記載のソースはサンプルです。ここで起動されたForm1の中で新たにスレッドを起動しています。その辺りになにか関係があるんじゃないかと疑っています。 ただ、デバッグ時には想定通りの動きをするのにexeを直接実行した際には別のダイアログ(アプリケーションで予期しない例外が~)が表示されることが不思議だったのでそういう事象に心当たりのある方がいらっしゃればと思い質問させていただきました。
Zuishin

2018/10/30 04:27

問題が再現するソースを新しく作ってみてください。普通はスレッドがあっても関係ないはずです。
shun_kuwa

2018/10/30 06:04

質問にソースを追記しました。 もしお時間あればご確認いただけますでしょうか。
Zuishin

2018/10/30 07:02

システムダイアログが出る前に AppDomain.CurrentDomain.FirstChanceException で独自の処理をすることはできますが、ダイアログを出さない方法はまだ調査中です。 見つかるのは、UnhandledException で終了を前提としたものばかりなので、UnhandledException が起こってしまった後は終了するよりほか手の施しようがないのかもしれません。 新スレッドで ShowDialog() を呼び出すところを try-catch すれば良いかとも考えたのですが、それもダメでした。 おそらくは、メッセージループを独自に実装し、メッセージを送る処理を try-catch することでキャッチできると思いますが、困難が予想されます。 独自スレッドを使わず、ShowDialog() の代わりに Show() を使って非同期処理に置き換えることができるなら、それが一番楽だと思います。
shun_kuwa

2018/10/30 08:10

丁寧に回答いただきありがとうございます。 やはり例外をまとめて処理ということができなさそうと分かっただけでも十分です。 既存システムの保守作業のため、ShowDialog()をShow()に変更するのも難しいかもしれませんが、まずは影響範囲の調査から行ってみます。ありがとうございました。
guest

0

その出る例外というのはどういう例外なんでしょうか。
それを提示してくれないと回答のしようがないかと。

想定できていない部分で例外が発生したときに、ユーザに分かりやすいメッセージを表示したいと悩んでいます。

これは無理な話、と思っておきましょう

投稿2018/10/30 03:08

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2018/10/30 03:31

ここではメッセージですが、出た例外のログを取ることはあります。 無理な話というのは、「ユーザーに分かりやすい」の部分ですか?
y_waiwai

2018/10/30 03:49

想定できない事象をユーザにわかりやすく、というのは土台無理な話ということですね。 「想定しないエラーが発生しました」とだせばいいというならいいんですけど 結局、例外のメッセージはどう出たのかを求めて走り回ることになります
Zuishin

2018/10/30 03:52

メッセージの表示自体が無理なようにも読めるので、確認でした。
shun_kuwa

2018/10/30 04:16

言葉足らずですみません。 要するに、「アプリケーションでハンドルされていない例外が~」というダイアログではなく、こちらで用意した「エラーが発生しているのシステム管理者に連絡してください。その際以下のメッセージ内容を伝えてください。~以下例外のメッセージなど参考情報~」みたいな内容のメッセージを表示したいということでした。 想定していない例外に対して、ユーザ側だけで解消できるようにという意図ではありませんのでご理解ください。 なお、発生する例外については「この○○という例外が出て困っている」という話ではなく「どんな例外が出てもどこか一か所で拾うことができないかを模索している」という内容の質問ですので、そちらもご理解いただけると助かります。
shun_kuwa

2018/10/30 04:19

申し訳ないですが、上記も踏まえこちらは回答ではなく質問に対する補足説明依頼と受け取りましたので、回答の評価は下げさせていただきました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問