3

実行時にアセンブリをコンパイルしてロードする方法を見つけようとしています。基本的な意図は、それらをディスクではなくデータベースに保存することです。そこで、いくつかのコードを書きましたが、興味深い状況が見られました。これが私のコードです:

//SumLib
namespace SumLib
{
    public class SumClass
    {
        public static int Sum(int a, int b)
        {
            return a + b;
        }
    }
}


// Console app
class Program
{

    public static void AssemblyLoadEvent(object sender, AssemblyLoadEventArgs args)
    {

        object[] tt = { 3, 6 };
        Type typ = args.LoadedAssembly.GetType("SumLib.SumClass");
        MethodInfo minfo = typ.GetMethod("Sum");
        int x = (int)minfo.Invoke(null, tt);
        Console.WriteLine(x);
    }

    static void Main(string[] args)
    {

        AppDomain apd = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
        apd.AssemblyLoad += new AssemblyLoadEventHandler(AssemblyLoadEvent);

        FileStream fs = new FileStream("Sumlib.dll", FileMode.Open);
        byte[] asbyte = new byte[fs.Length];
        fs.Read(asbyte, 0, asbyte.Length);
        fs.Close();
        fs.Dispose();

//      File.Delete("Sumlib.dll");

        apd.Load(asbyte);

        Console.ReadLine();
    }
}

コードは削除行がコメントアウトされている状態で完全に実行されます。コメントを外すと、アプリケーション ドメインがアセンブリをロードし、AssemblyLoadEvent()メソッドが実行され、コンソールに 9 が表示されますが、メソッドが終了apd.Load()するとエラーがスローされます:「ロードできませんでした」ファイルまたはアセンブリ。」これは完全に合理的です。

AssemblyLoadEvent()問題は、ディスク上のアセンブリ ファイルなしでメソッドを実行するにはどうすればよいかということです。

メソッドが生のバイナリ データの助けを借りて何らかの方法で実行される場合、appdomain がLoad()メソッドを正常に終了させる方法はありますか?

4

3 に答える 3

6

アセンブリを正常に「newdomain」にロードし、まだ newdomain にあるイベント ハンドラーを呼び出します(イベント ハンドラーで現在のドメインを出力すると、これを確認できます)。最後に、返される戻り値を作成します。サンプル コードではその戻り値を無視しますが、それでも作成されます。逆シリアル化ではアセンブリを既定のドメインにも読み込む必要があるため、クロスドメイン マーシャリング中に例外が発生します。

mono からの例外コール スタックは次のとおりです。

  at System.AppDomain.Load (System.String assemblyString, System.Security.Policy.Evidence assemblySecurity, Boolean refonly) [0x00000] in <filename unknown>:0
  at System.AppDomain.Load (System.String assemblyString) [0x00000] in <filename unknown>:0
  at (wrapper remoting-invoke-with-check) System.AppDomain:Load (string)
  at System.Reflection.Assembly.Load (System.String assemblyString) [0x00000] in <filename unknown>:0
  at System.UnitySerializationHolder.GetRealObject (StreamingContext context) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.ObjectRecord.LoadData (System.Runtime.Serialization.ObjectManager manager, ISurrogateSelector selector, StreamingContext context) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.ObjectManager.DoFixups () [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in <filename unknown>:0
  at System.Runtime.Remoting.RemotingServices.DeserializeCallData (System.Byte[] array) [0x00000] in <filename unknown>:0
  at (wrapper xdomain-invoke) System.AppDomain:Load (byte[])
  at (wrapper remoting-invoke-with-check) System.AppDomain:Load (byte[])
  at Program.Main (System.String[] args) [0x00000] in <filename unknown>:0

編集: MSDNからの確認は次のとおりです。

現在のアプリケーション ドメインではないターゲット アプリケーション ドメインで Load を呼び出そうとすると、ターゲット アプリケーション ドメインでアセンブリが正常に読み込まれます。Assembly は MarshalByRefObject ではないため、このメソッドが読み込まれたアセンブリの Assembly を現在のアプリケーション ドメインに返そうとすると、共通言語ランタイムはアセンブリを現在のアプリケーション ドメインに読み込もうとし、読み込みが失敗する可能性があります。2 つのアプリケーション ドメインのパス設定が異なる場合、現在のアプリケーション ドメインに読み込まれるアセンブリは、最初に読み込まれたアセンブリとは異なる可能性があります。

于 2011-01-21T17:21:53.897 に答える
3

したがって、byte[] からアセンブリをロードしてメソッドを呼び出そうとしています。すべての依存関係に対して呼び出されるため、あなたが行った方法 (AssemblyLoad イベントの操作) はお勧めしません。

@Jester は、親ドメインから Load() を使用してアセンブリをロードすることについて正しいです。これを修正するには、次のようなラッパー クラスを使用することをお勧めします。

// Console app 
class Program 
{  
    public class AssemblyLoader : MarshalByRefObject
    {
        public void LoadAndCall(byte[] binary)
        {
            Assembly loadedAssembly = AppDomain.CurrentDomain.Load(binary);
            object[] tt = { 3, 6 };
            Type typ = loadedAssembly.GetType("SumLib.SumClass");
            MethodInfo minfo = typ.GetMethod("Sum", BindingFlags.Static | BindingFlags.Public);
            int x = (int)minfo.Invoke(null, tt);
            Console.WriteLine(x);
        }
    }

    static void Main()
    {
        AppDomain apd = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
        FileStream fs = new FileStream("Sumlib.dll", FileMode.Open);
        byte[] asbyte = new byte[fs.Length];
        fs.Read(asbyte, 0, asbyte.Length);
        fs.Close();
        fs.Dispose();
        File.Delete("Sumlib.dll");    

        AssemblyLoader loader = (AssemblyLoader)apd.CreateInstanceAndUnwrap(typeof(AssemblyLoader).Assembly.FullName, typeof(AssemblyLoader).FullName);
        loader.LoadAndCall(asbyte);
        Console.ReadLine();
      }
}  
于 2011-01-21T18:07:11.263 に答える
0

Shadow Copyパラメータを使用しないのはなぜですか? それはあなたを助けるかもしれません。

于 2011-01-21T18:00:11.347 に答える