【入門編】Windows Formsにおける非同期処理の例外伝播とUIスレッドへの安全な集約:AggregateExceptionの正しいハンドリング手順 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

こんにちは! Windows Formsアプリケーションの開発、日夜お疲れ様です。

マクロの記録や古いVBScriptの世界から一歩踏み出し、本格的なVisual Basic(特に.NETの世界)でUIを作り始めると、避けて通れないのが「非同期処理(Async / Await)」と「例外処理(エラーハンドリング)」の壁です。

「ボタンを押したら裏で重い処理を動かしたい(UIを固めたくない)」と思って `Async` や `Task.Run` を使ってみたものの、なぜかアプリが突然フリーズしたり、原因不明のクラッシュを起こしたりして頭を抱えていませんか?

今回は、非同期処理の裏側でこっそり起きている例外(エラー)がどう伝播し、なぜアプリをクラッシュさせるのか、そしてそれを完全に手なずけて安全にユーザーに伝えるための極意を、優しく、かつプロのアーキテクトの視点から紐解いていきます。

ここをクリアすれば、あなたの書くWindows Formsアプリは見違えるほど堅牢になりますよ。さあ、一緒にマスターしていきましょう!

—

1. なぜ非同期処理の例外はアプリをクラッシュさせるのか?

まずは、Windows Formsの心臓部である「UIスレッド」と、非同期処理が走る「別スレッド(ワーカースレッド)」の関係図を見てみましょう。

[ ユーザーの操作 ]
│
▼
┌───────────┐ Async / Await ┌─────────────────┐
│ UIスレッド │ ──────────────────────> │ ワーカースレッド │
│ (画面描画) │ │ (重い処理を実行) │
└───────────┘ └───────────┬─────┘
▲ │
│ 例外(エラー)が発生! ▼
│ (ここでキャッチし忘れると…) [ 💥 予期せぬ例外 ]
│ │
│ .NET 4.0〜 ──> TaskScheduler.UnobservedTaskException
│ .NET 4.5+ ──> アプリケーション全体が即死クラッシュ!

古い時代のVB.NETやVBAでは、エラーは「起きたその場所」で止まりました。しかし、現代の `.NET` におけるタスク(`Task`)の世界では、別スレッドで起きた例外は親スレッドへ「伝播」しようとします。

ここで `Try…Catch` の網をくぐり抜けた例外は、`.NET Framework / .NET Core` の仕様により、アプリケーション全体の強制終了(クラッシュ)を引き起こします。「えっ、ちゃんとTry Catchしたつもりなのに!」という罠がここに潜んでいるのです。

—

2. 複数の例外を包み込む「AggregateException」の正体

さらに話は複雑になります。`Task.WhenAll` などを使って複数の非同期処理を同時に走らせた場合、複数のスレッドで同時にエラーが起きることがあります。

このとき、 .NETは複数の例外をまとめて1つの袋に詰め込みます。これが `AggregateException`(集約例外) です。

  • 普通の例外 (`Exception`): 個別のエラー(例:ファイルがない、DBがつながらない)
  • 集約例外 (`AggregateException`): エラーの詰め合わせパック。中に複数の例外(`InnerExceptions`)がゴロゴロ入っている。

VB.NETで非同期処理を扱うときは、この「詰め合わせパック」を開封して、中身を取り出すスマートな手順が必要になります。

—

3. 【実践】安全に例外をキャッチしてUIに伝える設計パターン

百聞は一見に如かず。Windows Formsのフォーム上に配置したボタン(`Button1`)を起点として、安全に非同期処理を行い、エラーが発生した場合はクラッシュを防いでメッセージボックスで優しく教えるコードを見てみましょう。

以下のコードを、そのままあなたのフォームクラスに貼り付けてみてください。

Imports System.Threading.Tasks
Imports System.Windows.Forms

Public Class Form1

‘ ボタンクリックイベント(Asyncを付与して非同期メソッドにする)
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

‘ 1. ユーザー操作を防ぐためにボタンを無効化(二重実行防止)
Button1.Enabled = False

Try
‘ 2. 重い処理(または失敗するかもしれない処理)を別スレッドで実行
Await Task.Run(
Sub()
‘ ここであえてエラーを発生させてみます(例:ゼロ除算)
Dim x As Integer = 0
Dim y As Integer = 10 / x
End Sub)

‘ 成功時の処理
MessageBox.Show(“処理が正常に完了しました!”, “成功”, MessageBoxButtons.OK, MessageBoxIcon.Information)

Catch ex As AggregateException
‘ 3. 複数タスクの並列実行などで起きた「例外の詰め合わせパック」を処理
‘ Flatten()を使うことで、ネストしたAggregateExceptionを綺麗に1枚の平らなリストにします
Dim flattenedEx = ex.Flatten()

‘ ユーザー向けに分かりやすいエラーメッセージを構築
Dim errorMessages As String = “以下のエラーが発生しました:” & vbCrLf

For Each inner As Exception In flattenedEx.InnerExceptions
errorMessages & = “・” & inner.Message & vbCrLf
Next

‘ UIスレッドから安全にモーダルダイアログを表示
MessageBox.Show(errorMessages, “処理エラー (複数)”, MessageBoxButtons.OK, MessageBoxIcon.Error)

Catch ex As Exception
‘ 4. 通常の単一の非同期例外のキャッチ
‘ Awaitを使っている場合、ここでTask内の例外が美しくキャッチされます!
MessageBox.Show($”処理中にエラーが発生しました。{vbCrLf}{ex.Message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)

Finally
‘ 5. 成功しようが失敗しようが、必ずUIの状態を元に戻す
Button1.Enabled = True
End If

End Sub

End Class

—

4. コードのここがポイント! 押さえておきたい3つの極意

上記のコードには、プロのエンジニアが現場で必ず使う「安全なUI設計の知見」が詰まっています。

① `Await` キーワードが例外を「アンラップ」してくれる

`Await Task.Run(…)` と書くことで、別スレッド(ワーカースレッド)で発生した例外は、まるでその場で起きたかのように `Catch ex As Exception` に吸い上げられます。`Await` は単に処理を待つだけでなく、スレッド間の例外の橋渡しをする魔法の構文なのです。

② `AggregateException.Flatten()` でエラーの取りこぼしをゼロに

もし将来的に `Task.WhenAll` などを使って「複数のファイルを同時にダウンロードする処理」などに拡張したとき、複数のエラーが同時に発生する可能性があります。`ex.Flatten()` を挟んでおくことで、複雑に絡み合ったエラーの階層をフラットにし、全ての原因をもれなくユーザーに伝えることができます。

③ `Finally` ブロックでUIのデッドロック・操作不能を防ぐ

非同期処理で最も多いバグが、「エラーが起きた後にボタンが押せないまま固まる」という現象です。`Try…Catch` のあとに必ず `Finally` を置き、そこでボタンの有効化(`Button1.Enabled = True`)を行うことで、どんな結末を迎えてもUIが必ず安全な状態に復帰します。

—

まとめ:非同期処理の例外制御は「優しさ」の形

Visual BasicでのWindows Forms開発は、歴史が長い分、古い書き方と新しい非同期の書き方が混ざりやすい領域です。

しかし、今回紹介した 「`Async/Await` による例外のキャッチ」「`AggregateException.Flatten()` による複数エラーの分解」「`Finally` によるUIの確実な復帰」 の3ステップを押さえておけば、もう予期せぬクラッシュに怯える必要はありません。

安定して動作し、万が一エラーが起きても親切にユーザーを導いてくれるUI――それこそが、優れたエンジニアが作るアプリケーションの証です。

ここをクリアしたあなたなら、もうVB.NETの非同期処理で迷うことはありません。自信を持って、次の機能開発へ進んでくださいね!

タイトルとURLをコピーしました