Страницы

Поиск по вопросам

понедельник, 24 февраля 2020 г.

Как правильно использовать методы сборки в другом домене?

#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 IEnumerable GetNames() { 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());

Комментариев нет:

Отправить комментарий