読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

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

別のAppDomainで処理を実行するための方法メモ (AppDomain, ExecuteAssembly, DoCallBack, CreateInstanceAndUnwrap, MarshalByRefObject, Serializable)

C#


たまに別のAppDomainを作成して、処理を実行することがあるので
実行の方法について個人的にメモメモ。


方法はいくつかあるのですが、私がよく利用するのは以下の3つ。

  • AppDomain.ExecuteAssemblyを利用する。
  • AppDomain.DoCallbackを利用する。
  • AppDomain.CreateInstanceAndUnwrapを利用して、プロキシを取得し実行.


既に実行するためのアセンブリが存在している場合は、ExecuteAssemblyがお手軽。
でも、実行するだけなので細かい調整が出来ない。
DoCallBackは、デリゲートを渡せるので便利。
必要な処置を行った後、ExecuteAssemblyを呼び出したりする。
CreateInstanceAndUnwrapは、一番フレキシブルな方法。


以下、サンプルです。
予め、ExecuteAssemblyメソッドで利用するためのアセンブリを以下のように作成し
"AnotherAppDomain.exe"として配置しているとします。

using System;
using System.Collections.Generic;
using System.Linq;

namespace AnotherAppDomain
{
  class Program
  {
    static void Main()
    {
      new Program().Execute();
    }

    void Execute()
    {
      var domain = AppDomain.CurrentDomain.FriendlyName;
      Console.WriteLine("Run on AppDomain:{0}", domain);
    }
  }
}


呼び出し部分。

  #region AppDomainSamples-04
  public class AppDomainSamples04 : MarshalByRefObject, IExecutable
  {
    public void Execute()
    {
      //
      // AppDomainを利用して、別のAppDomainで処理を実行するための方法は、いくつか存在する。
      //
      // ・AppDomain.ExecuteAssemblyを利用する。
      // ・AppDomain.DoCallbackを利用する。
      // ・AppDomain.CreateInstanceAndUnwrapを利用して、プロキシを取得し実行.
      //
      var currentDomain = AppDomain.CurrentDomain;
      var anotherDomain = AppDomain.CreateDomain("AD No.2");
      
      //
      // AppDomain.ExecuteAssemblyを利用して実行.
      // 
      // ExecuteAssemblyメソッドには、アセンブリ名を指定する。
      // ここで指定するアセンブリは実行可能である必要があり、エントリポイントを持っている必要がある。
      //
      anotherDomain.ExecuteAssembly(@"AnotherAppDomain.exe");
      
      //
      // AppDomain.DoCallbackを利用する.
      //
      // DoCallbackは指定されたデリゲートを実行するためのメソッド.
      // 別のAppDomainのDoCallbackにデリゲートを渡す事により
      // 処理がそのアプリケーションドメインで実行される。
      //
      // 当然、値渡し(Serializable)と参照渡し(MarshalByRefObject)によって実行結果が異なる場合がある.
      //
      // Staticメソッド
      Console.WriteLine("----------[Static Method]--------");
      currentDomain.DoCallBack(CallbackMethod_S);
      anotherDomain.DoCallBack(CallbackMethod_S);
      Console.WriteLine("---------------------------------");
      
      // インスタンスメソッド.
      Console.WriteLine("---------[Instance Method]-------");
      currentDomain.DoCallBack(CallbackMethod);
      anotherDomain.DoCallBack(CallbackMethod);
      Console.WriteLine("---------------------------------");
      
      // 値渡し (Serializable)
      var byvalObj = new MarshalByVal();
      
      Console.WriteLine("---------[Serializable]----------");
      currentDomain.DoCallBack(byvalObj.CallbackMethod);
      anotherDomain.DoCallBack(byvalObj.CallbackMethod);
      Console.WriteLine("---------------------------------");
      
      // 参照渡し (MarshalByRefObject)
      // MarshalByRefObjectを継承しているため、以下の例では必ずデフォルトドメインで実行されることになる。
      var byrefObj = new MarshalByRef();
      
      Console.WriteLine("-------[MarshalByRefObject]------");
      currentDomain.DoCallBack(byrefObj.CallbackMethod);
      anotherDomain.DoCallBack(byrefObj.CallbackMethod);
      Console.WriteLine("---------------------------------");
      
      //
      // AppDomain.CreateInstanceAndUnwrapを利用する。
      // プロキシを取得して処理を実行する.
      //
      var asmName  = typeof(MarshalByRef).Assembly.FullName;
      var typeName = typeof(MarshalByRef).FullName;
      
      var obj = (MarshalByRef) anotherDomain.CreateInstanceAndUnwrap(asmName, typeName);
      
      Console.WriteLine("-------[CreateInstanceAndUnwrap]------");
      obj.CallbackMethod();
      Console.WriteLine("--------------------------------------");
      
      AppDomain.Unload(anotherDomain);
    }
    
    static void CallbackMethod_S()
    {
      Utils.PrintAsmName();
    }

    void CallbackMethod()
    {
      Utils.PrintAsmName();
    }
    
    [Serializable]
    public class MarshalByVal
    {
      public void CallbackMethod()
      {
        Utils.PrintAsmName();
      }
    }
    
    public class MarshalByRef : MarshalByRefObject
    {
      public void CallbackMethod()
      {
        Utils.PrintAsmName();
      }
    }
    
    static class Utils
    {
      public static void PrintAsmName()
      {
        var domain = AppDomain.CurrentDomain.FriendlyName;
        Console.WriteLine("Run on AppDomain:{0}", domain);
      }
    }
  }
  #endregion


実行すると以下のようになります。

  Run on AppDomain:AD No.2
  ----------[Static Method]--------
  Run on AppDomain:MySamples.exe
  Run on AppDomain:AD No.2
  ---------------------------------
  ---------[Instance Method]-------
  Run on AppDomain:MySamples.exe
  Run on AppDomain:MySamples.exe
  ---------------------------------
  ---------[Serializable]----------
  Run on AppDomain:MySamples.exe
  Run on AppDomain:AD No.2
  ---------------------------------
  -------[MarshalByRefObject]------
  Run on AppDomain:MySamples.exe
  Run on AppDomain:MySamples.exe
  ---------------------------------
  -------[CreateInstanceAndUnwrap]------
  Run on AppDomain:AD No.2
  --------------------------------------


あと、以前にも、AppDomainについてはいつくか記事を記述しているのでご参考までに。


以下、参考情報です。

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