#c_sharp #net #рефлексия
Если создать домен и загрузить в него сборку, а потом используя метод GetAssemblies, достать сборки, то выгружая домен, сборка остается в основном домене приложения и с ней ничего сделать нельзя, удалить например. Каким образом можно использовать методы сборки из чужого домена, при этом выгружая домен выгрузить и сборку из приложения? Вот код которым я это делаю: using System; using System.IO; using System.Reflection; namespace Test { internal static class Program { private static void Main() { var appDomain = AppDomain.CreateDomain( "test" ); AssemblyLoader.Run(appDomain); Console.WriteLine("Сборка загружена"); foreach ( var assembly in appDomain.GetAssemblies () ) { Console.WriteLine ( assembly.GetName () ); } Console.ReadLine(); AppDomain.Unload( appDomain ); Console.WriteLine("Домен выгружен"); //Не удается удалить сборку из каталога, она не выгрузилась! Console.ReadLine(); } } public static class AssemblyLoader { public static void Run( AppDomain appDomain ) { appDomain.DoCallBack( Invoke ); } private static void Invoke() { Assembly.LoadFile( Path.Combine( Environment.CurrentDirectory, "test.dll" ) ); } } }
Ответы
Ответ 1
Если вы передаёте объект из другого домена, вы этим самым загружаете его в ваш домен, чего вы как раз хотите избежать. Решением может быть передавать примитивные типы. Чтобы не ограничивать себя простыми колбеками, вам нужно воспользоваться MarshalByRefObject (они не копируются между доменами, а выполняются в том домене, где созданы). Итак, вот рабочий код. Это плагин, DomainsPlugin.dll: namespace DomainsPlugin { public class Plugin { public IEnumerableGetNames() { return AppDomain.CurrentDomain.GetAssemblies().Select(asm => asm.GetName()); } } } Ничего особого, просто рабочий метод. Теперь главная программа: namespace DomainsMain { class Program { static void Main(string[] args) { // в этом каталоге бежит программа var executableDir = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location); // это каталог, где лежит плагин. // у вас он будет лежать, понятно, в другом месте var pluginDir = Path.GetFullPath(Path.Combine( executableDir, "..", "..", "..", "DomainsPlugin", "bin", "Debug")); // оригинал плагина var pluginSourceFile = Path.Combine(pluginDir, "DomainsPlugin.dll"); // а сюда мы его скопируем, и отсюда будем загружать var pluginWorkingFile = Path.Combine(executableDir, "DomainsPlugin.dll"); // копируем File.Copy(pluginSourceFile, pluginWorkingFile, overwrite: true); // окей, теперь новый домен var pluginDomain = AppDomain.CreateDomain("Plugin"); // теперь, создаём экземпляр нашего класса в новом домене // у нас на него реально лишь прокси-объект var resident = (Resident)pluginDomain.CreateInstanceAndUnwrap( Assembly.GetExecutingAssembly().FullName, "DomainsMain.Resident"); // получили данные resident.Obtain(); // выводим их foreach (var r in resident.Result) Console.WriteLine(r.ToString()); // выгружаем домен... AppDomain.Unload(pluginDomain); // ... и удаляем плагин File.Delete(pluginWorkingFile); } } // простой класс, будет выполняться в другом домене public class Resident : MarshalByRefObject { public List Result; public void Obtain() { // загружаем библиотеку var asm = Assembly.LoadFrom("DomainsPlugin.dll"); var type = asm.GetType("DomainsPlugin.Plugin"); // и вызываем функцию из неё через рефлексию // можно было бы закастить к интерфейсу, если объявить его в // этой или другой общей dll-ке var p = Activator.CreateInstance(type); var method = type.GetMethod( "GetNames", BindingFlags.Instance | BindingFlags.Public); var result = (IEnumerable )method.Invoke(p, null); // сохраняем результат. это поле будет доступно из первоначального домена Result = result.ToList(); } } } На моей машине из-под Visual Studio выдаёт: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Microsoft.VisualStudio.HostingProcess.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a DomainsMain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 DomainsPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Обновление: резидентная функция (Obtain()) вполне может возвращать значение. Но это значение должно быть сериализируемым, чтобы «протиснуться» между доменами. Для нашего примера, подправляем класс Resident: public class Resident : MarshalByRefObject { public List Obtain() // <-- поменяли возвращаемый тип { // и убрали свойство Result // загружаем библиотеку var asm = Assembly.LoadFrom("DomainsPlugin.dll"); var type = asm.GetType("DomainsPlugin.Plugin"); // и вызываем функцию из неё через рефлексию // можно было бы закастить к интерфейсу, если объявить его в // этой или другой общей dll-ке var p = Activator.CreateInstance(type); var method = type.GetMethod( "GetNames", BindingFlags.Instance | BindingFlags.Public); var result = (IEnumerable )method.Invoke(p, null); return result.ToList(); } } Теперь вызов из основного кода выглядит так: // получили данные var result = resident.Obtain(); // выводим их foreach (var r in result) Console.WriteLine(r.ToString());
Комментариев нет:
Отправить комментарий