いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

AppDomainクラスのDomainUnloadイベントとProcessExitイベントについて (AppDomain, DomainUnload, ProcessExit, タイムアウト時間, 2秒, 既定のAppDomain)


知ってると、ちょっと便利かもしれないTips。メモメモ。


AppDomainクラスには、アンロード時に発行されるDomainUnloadイベントがあります。
このイベントには、注意点がありまして

既定のアプリケーションドメインの場合は、イベントが発行されない。

という点があります。有効となるのは、自分で作成したアプリケーションドメインの場合です。
既定のアプリケーションドメインは、Unload出来ない(実行するとエラーとなる)ので、当たり前といえば当たり前ですが。


で、既定のアプリケーションドメインの場合はどうするのかという事になるのですが
以下のイベントを利用します。

public event EventHandler ProcessExit


ちなみに、このイベントは自分で作成したアプリケーションドメインの場合でもハンドルできます。
てことは、DomainUnloadイベントとProcessExitイベントの両方をハンドルすることが出来ます。


その場合、以下のようにイベントが発行されます。

  • AppDomain.Unloadメソッドで、対象アプリケーションドメインをアンロードするとDomainUnloadイベントが発生される。ProcessExitイベントは発生しない。
  • AppDomain.Unloadメソッドを呼ばずに、処理を終了するとProcessExitイベントが発生する。


以下、サンプルです。

  #region AppDomainSamples-02
  /// <summary>
  /// AppDomainクラスのサンプルです。
  /// </summary>
  public class AppDomainSamples02 : MarshalByRefObject, IExecutable
  {
    public void Execute()
    {
      AppDomain defaultDomain = AppDomain.CurrentDomain;
      AppDomain anotherDomain = AppDomain.CreateDomain("AnotherAppDomain");
      
      //
      // DomainUnloadイベントのハンドル.
      //
      // 既定のアプリケーションドメインでは、Unloadは登録できるが発行されることは
      // 無いので、設定する意味がない.
      //defaultDomain.DomainUnload += AppDomain_Unload;
      anotherDomain.DomainUnload += AppDomain_Unload;
      
      //
      // ProcessExitイベントのハンドル.
      //
      defaultDomain.ProcessExit += AppDomain_ProcessExit;
      anotherDomain.ProcessExit += AppDomain_ProcessExit;
      
      //
      // 既定のアプリケーションドメインをアンロードしようとするとエラーとなる.
      // ** appdomain をアンロード中にエラーが発生しました。 (HRESULT からの例外: 0x80131015) **
      //AppDomain.Unload(defaultDomain);
      
      //
      // AppDomain.Unloadを呼び出すと、DomainUnloadイベントが発生する.
      // AppDomain.Unloadを呼び出さずにプロセスが終了させようとすると
      // ProcessExitイベントが発生する。両方のイベントが同時に発生することは無い.
      //
      // 以下をコメントアウトすると、ProcessExitイベントが発生する.
      //
      AppDomain.Unload(anotherDomain);
    }
    
    void AppDomain_Unload(object sender, EventArgs e)
    {
      AppDomain domain = sender as AppDomain;
      Console.WriteLine("AppDomain.Unload: {0}", domain.FriendlyName);
    }
    
    void AppDomain_ProcessExit(object sender, EventArgs e)
    {
      //
      // ProcessExitイベントには、タイムアウトが存在する。(既定は2秒)
      // 以下、MSDNの記述.
      // (http://msdn.microsoft.com/ja-jp/library/system.appdomain.processexit.aspx)
      //
      // 「プロセス シャットダウン時における全ファイナライザーの合計実行時間が限られているように、ProcessExit の
      // すべてのイベント ハンドラーに対して割り当てられる合計実行時間も限られています。 既定値は 2 秒です。」
      //
      // 以下のコメントを外して実行すると、タイムアウト時間を過ぎるので
      // イベントをハンドルしていても、後続の処理は実行されない。
      //
      // わざとタイムアウト時間が過ぎるように待機.
      //Console.WriteLine("AppDomain.ProcessExit Thread.Sleep()");
      //Thread.Sleep(TimeSpan.FromSeconds(3));
      
      AppDomain domain = sender as AppDomain;
      Console.WriteLine("AppDomain.ProcessExit: {0}", domain.FriendlyName);
    }
  }
  #endregion


実行結果は、以下になります。MySamples.exeが既定のアプリケーションドメインです。

  AppDomain.Unload: AnotherAppDomain
  AppDomain.ProcessExit: MySamples.exe


上記サンプルにて、手動で作成したアプリケーションドメイン (anotherDomain)をUnloadせずに
処理を終了すると、以下のようになります。

  AppDomain.ProcessExit: MySamples.exe
  AppDomain.ProcessExit: AnotherAppDomain


尚、上記のサンプルにもコメントで記載している通り、ProcessExitイベントには
タイムアウト制限があります。既定では2秒となっています。2秒以内に処理を終えないと
タイムアウトと見なされ、処理が打ち切られます。


以下は、上記のサンプルのAppDomain_ProcessExitメソッドにて
わざとタイムアウトするようにして、実行した結果です。

  AppDomain.Unload: AnotherAppDomain
  AppDomain.ProcessExit Thread.Sleep()

後続の処理が打ち切られているのが分かります。


以下、参考情報です。

================================
過去の記事については、以下のページからご参照下さい。