はじめに
いつも脳死で App.xaml.cs に実装している内容の備忘録です。
改訂履歴
- 2026/10/08 : 初版公開。
本文
1. 環境
- .NET 8.0
- Visual Studio Community 2022
2. 実装
事実上のエントリポイントとなる App.xaml.cs に Mutex を使用した二重起動検証と、未処理例外の捕捉処理を実装しています。
| ハンドラ | 説明 |
|---|---|
DispatcherUnhandledException |
WPF の Dispatcher 上で処理されずに残った例外を捕捉する。e.Handled = true を設定すると、アプリケーションの終了を回避して処理を継続できる(できないこともあるらしい)。 |
AppDomain.CurrentDomain.UnhandledException |
アプリケーションドメイン内で未処理となった例外を通知する。プロセス終了前のログ記録などに使用する。通常、このイベントで例外を処理しても、アプリケーションの終了は回避できないらしい。 |
TaskScheduler.UnobservedTaskException |
Task 内で発生した例外が観測されないまま、タスクがガベージコレクションの対象になった際などに発生する。e.SetObserved() で例外を観測済みとして扱える。 |
App.xaml.cs
using System.Reflection;
using System.Windows;
using System.Windows.Threading;
namespace WpfExample;
/// <summary>
/// Windows Presentation Foundation アプリケーションを表します。
/// </summary>
public partial class App : Application
{
private static Mutex? s_mutex;
private static string s_assemblyName = "Application";
/// <summary>
/// <see cref="Application.Startup"/> イベントを処理します。
/// </summary>
/// <param name="e">イベントデータを含む <see cref="StartupEventArgs"/>。</param>
protected override void OnStartup(StartupEventArgs e)
{
try
{
base.OnStartup(e);
s_assemblyName = Assembly
.GetEntryAssembly()?
.GetName()
.Name ?? s_assemblyName;
s_mutex = new Mutex(
initiallyOwned: true,
name: s_assemblyName,
createdNew: out bool createdNew);
if (!createdNew)
{
MessageBox.Show(
"The application is already running.",
s_assemblyName,
MessageBoxButton.OK,
MessageBoxImage.Information);
Shutdown();
return;
}
DispatcherUnhandledException +=
OnDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException +=
OnUnhandledException;
TaskScheduler.UnobservedTaskException +=
OnUnobservedTaskException;
}
catch (Exception ex)
{
HandleException(ex);
Shutdown();
}
}
/// <summary>
/// <see cref="Application.Exit"/> イベントを処理します。
/// </summary>
/// <param name="e">イベントデータを含む <see cref="ExitEventArgs"/>。</param>
protected override void OnExit(ExitEventArgs e)
{
try
{
s_mutex?.ReleaseMutex();
}
catch
{
// 無視。
}
finally
{
s_mutex?.Dispose();
s_mutex = null;
}
base.OnExit(e);
}
private void OnDispatcherUnhandledException(
object sender,
DispatcherUnhandledExceptionEventArgs e)
{
HandleException(e.Exception);
// アプリを終了させない。
e.Handled = true;
}
private void OnUnhandledException(
object sender,
UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception ex)
{
HandleException(ex);
}
}
private void OnUnobservedTaskException(
object? sender,
UnobservedTaskExceptionEventArgs e)
{
HandleException(e.Exception);
// 例外処理したことをランタイムに通知する。
e.SetObserved();
}
private static void HandleException(Exception exception)
{
try
{
// ログやメッセージの表示など、例外処理を実装する。
MessageBox.Show(
$"An unexpected error has occurred."
+ Environment.NewLine
+ exception,
s_assemblyName,
MessageBoxButton.OK,
MessageBoxImage.Error);
}
catch
{
// 無視。
}
}
}
おわりに
本当に脳死で実装しています。DI コンテナやログも入れてもよかったかも。