はじめに
PC から 2 年半くらい前に投稿した記事を発掘したので、再 UP です。
改訂履歴
- 2026/08/04 : 再投稿。
- 2024/03/14 : 初版公開。
本文
1. 環境
- .NET Framework 4.8.1
- Visual Studio Community 2022
2. 実装
例外捕捉時、メッセージを表示していますが、実際はログ出力にすることが多いです。
using System;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace App
{
/// <summary>
/// アプリケーションのエントリーポイントを定義します。
/// </summary>
internal static class Program
{
/// <summary>
/// アプリケーションのメインエントリポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.ThreadException += Application_ThreadException;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mutex = new Mutex(true, Application.ProductName, out bool createdNew);
try
{
if (!createdNew)
{
MessageBox.Show(
text: "このアプリケーションは既に起動中です。",
caption: "Error",
buttons: MessageBoxButtons.OK,
icon: MessageBoxIcon.Error);
return;
}
Application.Run(new SampleForm());
}
finally
{
if (createdNew)
{
mutex.ReleaseMutex();
}
mutex.Close();
}
}
/// <summary>
/// UI スレッドで発生した未処理の例外を処理します。
/// </summary>
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
var text = new StringBuilder();
text.AppendLine(e.Exception.Message);
text.AppendLine("スタックトレース:");
text.AppendLine(e.Exception.StackTrace);
MessageBox.Show(
text: text.ToString(),
caption: "Error",
buttons: MessageBoxButtons.OK,
icon: MessageBoxIcon.Error);
}
}
}
3. 動作確認
動作確認用の画面を作成したので、実際に確認していきます。なお、二重起動の防止処理については今回は省略します。
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace App
{
public partial class SampleForm : Form
{
public SampleForm() => InitializeComponent();
// UI スレッドで例外
private void ThrowExceptionButton_Click(object sender, EventArgs e)
=> int.Parse(null);
// async/await で例外
private async void AwaitTaskRunButton_Click(object sender, EventArgs e)
=> await Task.Run(() => int.Parse(null));
// Task.Wait() で例外
private void TaskRunWaitButton_Click(object sender, EventArgs e)
=> Task.Run(() => int.Parse("Hoge")).Wait();
// Task.Run() で例外
private void TaskRunButton_Click(object sender, EventArgs e)
=> Task.Run(() => int.Parse("2147483648"));
}
}
見せる意味はないですが、一応画面はこんな感じです。
3-1. UI スレッドでの例外発生
未処理の例外を捕捉できていて良い感じです。
3-2. サブスレッドでの例外発生 - await
こちらも良い感じです。
3-3. サブスレッドでの例外発生 - Wait()
これは良い感じのように見えて、例外メッセージがおかしいですね。「1 つ以上のエラーが発生しました。」と表示されており、どの例外を捕捉したのか分かりません。
これは Task.Wait() を使用した場合、複数の例外が AggregateException にまとめられる仕様のためです。詳細は下記の記事をご覧ください(丸投げ)。
個人的にはそもそも開発時に Task.Wait() や Task.Result の使用を非推奨(というか基本的に禁止)にしているため、今回このケースには対応していません...!非推奨の理由は下記の MSDN 参照です(丸投げ)。
async/await を使ってください...!
3-4. サブスレッドでの例外発生 - 待たない場合
このケースでは、そもそも例外を捕捉できません。待たない場合は下記のように try catch で囲んでも捕捉できません...。
private void TaskRunButton_Click(object sender, EventArgs e)
{
try
{
Task.Run(() => int.Parse("2147483648"));
}
catch (Exception ex)
{
// ※ ここには入ってこない。
MessageBox.Show(ex.ToString());
}
}
おわりに
当時の僕曰く「コピペ用にサクッと書き切るつもりが、非同期処理の例外関連で分量が多くなりました。調べ物が少し大変でしたが、勉強になったのでよかったです...!」とのことです。がんばれ、僕。