Application.UnhandledExceptionについて

さて、Silverlightにはフックされなかった例外の最終地点用にApplicationクラスにUnhandledExceptionイベントが用意されているのですが、こいつをちゃんと使えるようにどう動くか調べてみました。具体的には例外が発生するタイミングによって画面がどうなるかです。

UnhandledExceptionイベントの中を以下のように書き換えます。

private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) {
  e.Handled = true;
  ChildWindow window = new ChildWindow(); 
  window.Content = e.ExceptionObject.ToString();
  window.Show();
}

例外をフックしたらChildWindowが表示されるようにしてます。もちろん、EventArgsのHandledにはtrueを設定しておきます。こいつが無いとフックされなかったのと同じ動きでブラウザまで例外が通知されてしまいます。

まずは、MainPageのLoadedイベントで例外を起こしてみます。

public MainPage() {
  InitializeComponent();
  this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e) {
  throw new Exception("MainPageのLoadedで例外発生");
}

実行すると・・・

無事にハンドルされてます。


次は、Application.Startupイベントの中でRootVisualに設定された後での例外です。

private void Application_Startup(object sender, StartupEventArgs e) {
  this.RootVisual = new MainPage();
  throw new Exception("StartupでRootVisualに設定された後に例外発生");
}

実行すると・・・

こちらも無事にハンドルされてます。


続いては、Application.Startupイベントの中でRootVisualに設定される前での例外です。

private void Application_Startup(object sender, StartupEventArgs e) {
  throw new Exception("StartupでRootVisualに設定される前での例外発生");
  this.RootVisual = new MainPage();
}

実行すると・・・

100% でクルクル回り続けます。

詳しくはまたいつかブログに書くかもしれませんが、SilverlightのローディングはRootVisualにUIElementが設定されるまで表示されます。この場合、RootVisualに値が設定される前に例外が発生したため、100%で回り続けることになります。ChildWindowが表示されないのは、RootVisualに値が設定されていないため画面表示の準備がまだできていないという判断で表示されていないと思われます。


さて、最後はApplicationのコンストラクタで例外を発生させてみます。

public App() {
  this.Startup += this.Application_Startup;
  this.Exit += this.Application_Exit;
  this.UnhandledException += this.Application_UnhandledException;

  InitializeComponent();

  throw new Exception("Applicationのコンストラクタでの例外発生");
}

実行すると・・・

ブラウザのダイアログがあがってしまってます。UnhandledExceptionがそもそも発生してません。


まとめると以下。

  • Applicationのコンストラクタで例外は絶対に発生させてはいけない。
  • RootVisualにUIElementが設定される前の例外はダイアログなどのUIは表示できない。
    • IsolateStorageなどに落とせるかどうかは未確認
    • 通常のアプリではダイアログは上げるため、やはりここでも例外は発生させてはいけない。


ということで、実装方針としてはRootVisualにUIElementが設定される前に何か処理はしない。かつ、設定するUIElementのコンストラクタでは例外が出ないようにする。ってところでしょうか。