以前あげたC# .NETアプリケーション開発 徹底攻略 Listing2.26への疑問 – SharpLab.というエントリに対して、なちゃさんから、

なちゃ 09-09-23 09:15:19 JST

本来、デリゲートでBeginInvokeした場合は、必ずEndInvokeする必要がありますね。
リソースリークなども発生する危険があります。
※デリゲート以外でもBegin~系は基本的には同じですが

EndInvokeさえすれば、きちんと発生した例外がスローされてきます。
けして例外が握りつぶされているのではなく、正しく使っていないために握りつぶされているように見えているだけですね。
ちょっと書籍の記述がまずいと思います。

という指摘がありました。なるほど!指摘の通り、Begin EndパターンなのでEndInvokeを必ず呼び出す必要がありました。恥ずかしながらすっかり失念していました。。というわけでEndInvokeを使うように修正。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Threading;

namespace SharpLab.ThreadExceptionTest {
	static class Program {

		static Form form;

		/// <summary>
		/// アプリケーションのメイン エントリ ポイントです。
		/// </summary>
		[STAThread]
		static void Main() {

			Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
			Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(Program_UnhandledException);

			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			form = new Form();
			Application.Run(new Form1());
		}

		static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
			MessageBox.Show(e.Exception.Message);
		}

		static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
			Exception ex = (Exception)e.ExceptionObject;
			MessageBox.Show(ex.Message);
			Application.Exit();
			Environment.Exit(0);
		}

	}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace SharpLab.ThreadExceptionTest {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {

			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke((asyncResult) => {
				a.EndInvoke(asyncResult);
			}, null);
		}

		private void ThreadMethod() {
			//意図的に起こした例外
			Thread.Sleep(5000);
			throw new Exception("非同期スレッドで起こった例外です。");
		}

	}
}

image

こんな感じでしょうか。ちゃんとサブスレッドで例外が発生した時にUnhandledExceptionが呼び出されるようになりました!これで正しく確実な終了処理ができそうです。サブスレッドの処理内容をすべてtry~catchで括って必要に応じて例外をリスローする必要があると憶えていたのですが、そうではなかったのですね。いつもtry~catch書きながらなんかおかしい気がしていたのですが、これですっきりしました。なちゃさん、ありがとうございました。