8.1ストア・アプリではトップレベルでまとめて例外をトラップできるようになった。その実装方法を解説する。8アプリ)では1つだけ困ったことがあった。アプリのトップレベルでまとめて例外をトラップできなかったのである。8.1)ではそれが改善されている。そこで本稿では、アプリのトップレベルでまとめて例外をトラップする方法を解説する。8/8.1版)」からダウンロードできる。2013)が必要である。Windows(日本語版)*2を使用してプログラミングしている。2012)が必要である。C#では「async/await」であり、VBでは「Async/Await」であるが、小文字のみの表記とさせていただく。VBでは適宜読み替えていただきたい。Windows」から無償で入手できる。8)Windowsストアアプリでは、非同期処理を多用する。2012で導入されたasync/awaitキーワードによって非同期処理はごく簡潔に記述できるようになったので、多用するからといって手間が掛かるわけでもないし、コードの見通しが悪くなることもない*3。このようにasync/awaitキーワードは素晴らしいものだが、アプリのトップレベルで例外をトラップできないという問題があった。async/awaitキーワードについて詳しくは次の記事を参照してほしい。8アプリのコードを書いて確認しておこう。例外が発生したその場で対処しきれる場合はよいのだが、そうではなくアプリのトップレベルで対処したい場合がある。1カ所で済ませたい、また、例外をログとして送信する場合に1カ所にまとめて書きたい、といった場合だ。そこで、Applicationクラス(Windows.UI.Xaml名前空間)のUnhandledExceptionイベントを利用して、「App.xaml.cs」ファイルに次のようなコードを書く。2012で、Appクラスに太字の部分を追加する。DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION」を追記する必要がある(リリースビルドでは追記不要)。それでは、例外が発生するコードを書いて試してみよう。8)▼▽画面にはボタンを3つ配置する。順に「Button1」/「Button2」/「Button3」と名前を付け、それぞれにClickイベントハンドラーを用意する。1つ目のボタンのイベントハンドラーには、例外が発生するメソッドを呼び出すコードをasyncキーワードなしで記述してみる(次のコード)。Sub例外が発生するメソッドをasync無しで呼び出す(上:C#、下:VB)これで実行してボタンをタップしてみると、「例外を出すメソッド」で例外が発生し、それが先述したAppクラスのUnhandledExceptionイベントハンドラーでトラップされて、次の画像のようにメッセージダイアログが表示される。8)▼▽デバッグ実行では、例外が発生した時点でいったんブレークしてしまうが、そのまま実行を継続させる。このように、AppクラスのUnhandledExceptionイベントを利用すれば、トップレベルで例外をハンドリングできる。8で実行すると、async/awaitキーワードを使ったメソッドで発生した例外は、この方法ではトラップできないのだ。それも、メソッドが呼び出される過程のどこかでasync/awaitキーワードが使われていたらだめなのである。2つ目のボタンのイベントハンドラーには、asyncキーワードを付けて試してみよう。それ以外は、先ほどと同じコードを書く(次のコード)。Sub例外が発生するメソッドをasync付きのメソッドから呼び出す(上:C#、下:VB)awaitしているコードがないので警告が出るが、試しにやってみているだけなので無視してほしい。8上で実行してみると、例外をトラップできず、アプリは終了してしまう。逆に、イベントハンドラーの「async」を削り、「例外を出すメソッド」の方にasyncキーワードを付けてみても、やはり例外をトラップできずにアプリは終了してしまう。8ではAppクラスのUnhandledExceptionイベントで例外をトラップできないのだ。Windowsストアアプリは非同期処理を多用するので、実行経路のどこにもasync/awaitが無いという方が珍しいだろう。8では使えないに等しかったのだ。別スレッドで例外が発生した場合は、asyncキーワードの有無に関わらず、トラップできない。例外が発生したスレッドで例外をトラップし、元のスレッドに戻ってからリスロー(例外を投げ直す)しなければならない。これも確かめておこう(次のコード)。Sub別スレッドで発生した例外はUnhandledExceptionでトラップできない(上:C#、下:VB)例外が発生しているにも関わらず、メインスレッドでは例外をトラップできないし、アプリも終了しない。例外が発生したスレッドは、そこで打ち切られ、消滅する。このコードはasync/awaitキーワードを使っていないが、AppクラスのUnhandledExceptionイベントではトラップできず、しかもアプリは継続してしまう。例外はスレッドをまたいで伝播しないからである。「別スレッドで例外を出すメソッド」の末尾にコメントアウトしてある「task.Wait()」を追加すると、そのWaitメソッド内で別スレッドの例外をリスローしてくれるので、トラップできるようになる。改良されたUnhandledExceptionイベント(Win8.1)Win8.1では、AppクラスのUnhandledExceptionイベントが改良され、async/awaitキーワードを使っているメソッドで発生した例外もトラップされるようになった。コードは何も変更せず、そのままWin8.1で実行してみよう*4。8.1では例外がトラップされる(次の画像)。8ではトラップできず、アプリは終了させられていたものだ。8.1)このようにWin8.1ではAppクラスのUnhandledExceptionイベントで例外をトラップできるようになったので、活用していってほしい。8の環境からソースコードを持ってきてそのままビルドした。2013で新しくプロジェクトを作って、上と同様なコードを記述してほしい。アンマネージコードの例外もトラップできるUnhandledErrorDetectedイベント(Win8.1)さて、便利になったAppクラスのUnhandledExceptionイベントにも欠点がある。マネージコードで発生した例外しかトラップできないのだ。従って、DirectXなどのアンマネージコードを使うクラスライブラリで発生した例外はトラップできない。8.1では、CoreApplicationクラス(Windows.ApplicationModel.Core名前空間)にUnhandledErrorDetectedイベントが新設された。こちらは、アンマネージコードで発生した例外もトラップできる。2013でAppクラスに次のようなコードを記述すればよい。2013で、Appクラスに太字の部分を追加する。アプリを終了させないためには、MSDNのドキュメントにはe.UnhandledError.Handledにtrue/Trueを代入するように書かれているが、間違いである(セッターが無いので代入できない)。Propagateメソッドを実行して例外を取り出した時点で、e.UnhandledError.Handledはtrue/Trueに変わっている。8のものと全く同じに記述する。実行してみて、asyncメソッドで発生した例外もトラップできることを確かめてほしい。アンマネージコードを含むクラスライブラリを書いて、その中で発生した例外もトラップできることを確認したいところだが、長くなるので本稿では割愛させていただく。UnhandledErrorDetectedイベントを使うとアンマネージコードで発生した例外もトラップできるようになるのであって、別スレッドや別プロセスで発生した例外はやはりトラップできないので注意してほしい。WebViewコントロールで発生するJavaScriptのエラー*5は、別プロセスで発生しているものなのでトラップできない。このCoreApplicationクラスのUnhandledErrorDetectedイベントは、前述したAppクラスのUnhandledExceptionイベントと比べると、ちょっと書き方が面倒だ。必要に応じて使い分けていただきたい。WebViewコントロールで発生するJavaScriptのエラーについては次の記事を参照してほしい。8.1では、asyncメソッドで発生した例外もトップレベルで一括してトラップできるようになった。AppクラスのUnhandledExceptionイベントではマネージコードで発生した例外だけが、CoreApplicationクラスのUnhandledErrorDetectedイベントではアンマネージコードで発生した例外も含めてトラップできる。アンマネージコードで発生した例外は、トラップしてもアプリが終了してしまうこともあるので注意が必要だ。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。