Загрузка / выгрузка сборки в другом AppDomain

Мне нужно выполнить метод в сборке, загруженной во время выполнения. Теперь я хочу выгрузить эти загруженные сборки после вызова метода. Я знаю, что мне нужен новый AppDomain, чтобы я мог выгружать библиотеки. Но здесь возникает проблема.

Сборки, которые будут загружаться, - это плагины в моей структуре плагинов. У них вообще нет точки входа. Все, что я знаю, это то, что они содержат некоторые типы, реализующие данный интерфейс. Старый код, не относящийся к AppDomain, выглядит так (немного сокращен):

try
{
    string path = Path.GetFullPath("C:\library.dll");
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    Assembly asm = Assembly.LoadFrom(path);
    Type[] types = asm.GetExportedTypes();
    foreach (Type t in types)
    {
        if ((t.GetInterface("IStarter") != null) && !t.IsAbstract)
        {
            object tempObj = Activator.CreateInstance(t);
            MethodInfo info = t.GetMethod("GetParameters");
            if (info != null)
            {
                return info.Invoke(tempObj, null) as string;
            }
        }
    }
}
catch (Exception ex)
{
    MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name.StartsWith("MyProject.View,"))
    {
        string path = Path.GetFullPath("C:\view.dll"));
        return Assembly.LoadFrom(path);
    }
    return null;
}

Теперь я хочу, чтобы они загружались в собственный домен приложений:

try
{
    string path = Path.GetFullPath("C:\library.dll");
    AppDomain domain = AppDomain.CreateDomain("TempDomain");
    domain.AssemblyResolve += CurrentDomain_AssemblyResolve;  // 1. Exception here!!
    domain.ExecuteAssembly(path);  // 2. Exception here!!
    domain.CreateInstanceFrom(...);  // 3. I have NO clue, how the type is named.
    domain.Load(...);  // 4. I have NO clue, how the assembly is named.
    domain.DoCallBack(...); // 5. Exception here!!
    // ...
}
catch (Exception ex)
{
    MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

Как видите, я поставил 5 ящиков.

  1. Если я устанавливаю обработчик событий, я получаю исключение, что сборка (это консоль управления (mmc.exe) SnapIn. Не может быть найдена / загружена.

  2. ExecuteAssembly не находит точку входа (ну ее нет).

  3. Я понятия не имею, как назван этот тип. Как загрузить по интерфейсу?

  4. Аналогично 3. Как получить имя сборки?

  5. Та же ошибка, что и в 1.

Я думаю, что проблема может быть в консоли управления, или я просто не понимаю, что я делаю не так. Любая помощь приветствуется.

ОБНОВЛЕНИЕ 1

Я пробовал использовать опубликованное прокси-решение.

AppDomain domain = AppDomain.CreateDomain("TempDomain");
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(Assembly.GetAssembly(
    typeof(InstanceProxy)).FullName, typeof(InstanceProxy).ToString()) as InstanceProxy;
if (proxy != null)
{
    proxy.LoadAssembly(path);
}
AppDomain.Unload(domain);

public class InstanceProxy : MarshalByRefObject
{
    public void LoadAssembly(string path)
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        Assembly asm = Assembly.LoadFrom(path);
        Type[] types = asm.GetExportedTypes();
        // ...see above...
    }
}

Это тоже не работает. При попытке создать прокси-объект получаю исключение:

Could not load file or assembly 'MyProject.SnapIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Файл в сообщении об ошибке загружен в mmc (SnapIn). Есть идеи, как исправить эту ошибку? AppDomain.AssemblyResolve не вызывается (ни в старом, ни в новом домене).

ОБНОВЛЕНИЕ 2

Теперь я попробовал решение с помощью AppDomainSetup. Теперь исключение изменилось на:

Could not load file or assembly 'file:///C:/Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)

Любая идея?


person Scoregraphic    schedule 25.01.2010    source источник
comment
Вы пытались создать класс, который наследуется от MarshallByRef, чтобы он действовал как прокси и позволял ему выполнять эту грязную работу в контексте нового домена приложения?   -  person Rubens Farias    schedule 25.01.2010
comment
Будет ли этот класс в сборке для загрузки или в уже загруженной сборке (которая пытается загрузить другие)?   -  person Scoregraphic    schedule 25.01.2010
comment
Не используйте CreateInstanceAndUnwrap, используйте CreateInstanceFromAndUnwrap.   -  person BFree    schedule 25.01.2010
comment
Ах, это помогло. Спасибо большое.   -  person Scoregraphic    schedule 25.01.2010
comment
Привет, это с точки зрения архитектуры. Не могли бы вы подробно описать сценарий, в котором вы используете этот подход для создания нового домена приложения с последующей выгрузкой после вызова нескольких участников? Спасибо!   -  person thewpfguy    schedule 27.06.2012
comment
У нас есть хост плагинов и неопределенное количество надстроек для работы. Надстройки должны быть выгружены после запуска (загружены только при определенных событиях), чтобы их можно было обновить без перезапуска узла службы.   -  person Scoregraphic    schedule 28.06.2012
comment
Не могли бы вы опубликовать полный пример рабочего кода? Я пытаюсь сделать что-то подобное, но не могу заставить его работать.   -  person deadlydog    schedule 30.10.2014
comment
По запросу deadlydog я загрузил полностью рабочий пример проекта на tooldesign.ch/pub/AppDomainTester.zip   -  person Scoregraphic    schedule 31.10.2014


Ответы (3)


Попробуй это:

namespace SeperateAppDomainTest
{
    class Program
    {
        static void Main(string[] args)
        {
            LoadAssembly();
        }

        public static void LoadAssembly()
        {
            string pathToDll = Assembly.GetExecutingAssembly().CodeBase;
            AppDomainSetup domainSetup = new AppDomainSetup { PrivateBinPath = pathToDll };
            var newDomain = AppDomain.CreateDomain("FooBar", null, domainSetup);
            ProxyClass c = (ProxyClass)(newDomain.CreateInstanceFromAndUnwrap(pathToDll, typeof(ProxyClass).FullName));
            Console.WriteLine(c == null);

            Console.ReadKey(true);
        }
    }

    public class ProxyClass : MarshalByRefObject { }
person BFree    schedule 25.01.2010

Взгляните на этот предыдущий ответ: Как загрузить сборку в другой домен приложений в Windows Mobile (.NET CF)?. Этот ответ создает прокси-класс, который запускается в новом контексте AppDomain, поэтому вы можете полностью контролировать свою инициализацию.

Вы можете создать Start() метод в ServiceApplicationProxy классе и просто вызывать его в обычном режиме из вашего хостера с proxy.Start().

person Rubens Farias    schedule 25.01.2010
comment
Спасибо за Ваш ответ. Я пробовал это, но все равно получаю ошибки. См. Мое обновление в исходном вопросе. - person Scoregraphic; 25.01.2010

https://msdn.microsoft.com/en-us/library/3c4f1xde%28v=vs.110%29.aspx

указывает, что

typeName Тип: System.String

The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName

имущество.

Поэтому попробуйте позвонить с полностью квалифицированным именем, вместо использования typeof(InstanceProxy).ToString() используйте строку / текст "<<Namespace>>.InstanceProxy"

как показано ниже

InstanceProxy proxy = domain.CreateInstanceAndUnwrap(path, "<<Namespace>>.InstanceProxy") as InstanceProxy;
person Khavasi    schedule 24.11.2016
comment
Разве typeof(InstanceProxy).FullName не будет лучше, чем жесткое кодирование пространства имен + typename? - person Maarten; 24.11.2016