2011-01-21 10 views
3

実行時にアセンブリをコンパイルしてロードする方法を見つけようとしています。基本的な目的は、ディスクに保存されていないデータベースにそれらを保存することです。だから私はいくつかのコードを書いたが、面白い状況を見た。ここに私のコードは次のとおりです。AppDomain.Load()の面白いエラー

//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()メソッドを正常に終了する方法はありますか?

+0

バイト[]からアセンブリをロードしようとしています。そうですか? –

+0

@Sam B:そうです。 –

答えて

2

したがって、バイト[]からアセンブリをロードしてメソッドを呼び出しようとしています。すべての依存関係に対して呼び出されるため、(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(); 
     } 
} 
+0

興味深いことにしたがって、appdomainをアンロードすると、メモリからアセンブリが削除されますか? –

+0

@sad_man:そうです –

+0

@sad_man:リフレクションでターゲットアセンブリを呼び出すので、リフレクションの問題を検出するためにLoadAndCallの中にtry-catchブロックを配置することをお勧めします。 –

6

"newdomain"にアセンブリの細かい部分をロードし、新しいドメインにまだ残っているイベントハンドラを呼び出します(イベントハンドラで現在のドメインを印刷すると確認できます)。最後に、戻される戻り値を作成します。サンプルコードでその戻り値を無視しますが、それはまだ作成されます。逆シリアル化ではアセンブリを既定のドメインに読み込みたいので、クロスドメインマーシャリング中に例外が発生します。

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からです:

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

0

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

+0

主に、ShodowがコピーしたアセンブリをApplicationBaseで指定されたアプリケーションディレクトリまたはそのサブディレクトリに格納する必要があるためです。このようにして、アセンブリをデータベースに格納し、必要に応じてアセンブリをロードすることもできます。 –

関連する問題