2011-01-21 1 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

hmmm интересный. Итак, выгрузка appdomain также удалит сборку из памяти? –

+0

@sad_man: Да, это будет –

+0

@sad_man: Поскольку вы вызываете в целевую сборку путем отражения, я предлагаю установить блок try-catch внутри LoadAndCall для обнаружения проблем с отражением. –

6

Он загружает сборку в «новый домен» и вызывает обработчик событий еще в новом домене (это можно проверить, если вы печатаете текущий домен в обработчике событий). Наконец, он возвращает возвращаемое значение. Вы игнорируете это возвращаемое значение в своем примере кода, но оно все еще создано. Исключение происходит во время междоменной сортировки, потому что десериализация хочет загрузить сборку в домен по умолчанию.

Вот стек вызовов исключение из моно:

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 

Edit: здесь подтверждение MSDN:

Попытка вызвать нагрузки на домене целевого приложения, не текущее приложение домен приведет к успешной загрузке сборки в целевом домене приложения. Поскольку Assembly не является MarshalByRefObject, когда этот метод пытается вернуть сборку для загруженной сборки в текущий домен приложения, среда выполнения обычного языка попытается загрузить сборку в текущий домен приложения, и загрузка может завершиться неудачей. Сборка, загруженная в текущий домен приложения, может отличаться от сборки, которая была загружена первой, если параметры пути для двух доменов приложения различны.

0

Почему бы вам не использовать параметр Shadow Copy? Это может помочь вам.

+0

Главным образом потому, что для этого требуются сборки, то есть скопированные копии, которые должны храниться в каталоге приложения или его подкаталогах, указанных в ApplicationBase. Таким образом, я могу даже хранить сборки в базе данных и загружать их, когда это необходимо. –