Загрузить текущую сборку в другой AppDomain

Я создал AppDomain С другой директории. Однако я не могу загрузить текущую исполняемую сборку в другой AppDomain без копии текущей исполняемой сборки в базовом каталоге. Я даже пытался загрузить его из байтов.

Я не получаю исключения, когда пытаюсь загрузить, но когда я пытаюсь использовать:

domain.DoCallBack(new CrossAppDomainDelegate(... 

Я:

не удалось загрузить файл или сборку ........... Система не может найти указанный файл.

мой код выглядит следующим образом:

private static void SaveAssemblies(Assembly ass, List<byte[]> assemblyByteList)
{
    AssemblyName[] assNames = ass.GetReferencedAssemblies();
    foreach (AssemblyName assName in assNames)
    {
        Assembly referedAss = Assembly.Load(assName);
        if (!referedAss.GlobalAssemblyCache)
        {
            SaveAssemblies(referedAss, assemblyByteList);
        }
    }
    byte[] rawAssembly = File.ReadAllBytes(ass.Location);
    assemblyByteList.Add(rawAssembly);
}

public static AppDomain CreateAppDomain(string dir, string name)
{
    AppDomainSetup domainSetup = new AppDomainSetup();
    domainSetup.ApplicationBase = dir;
    domainSetup.ApplicationName = Path.GetFileName(dir);
    domainSetup.PrivateBinPath = Path.Combine(dir, "Libs");

    AppDomain domain = AppDomain.CreateDomain(name, null, domainSetup);
    //Load system assemblies needed for the module
    List<byte[]> assemblyByteList = new List<byte[]>();
    SaveAssemblies(Assembly.GetExecutingAssembly(), assemblyByteList);

    foreach (byte[] rawAssembly in assemblyByteList)
        domain.Load(rawAssembly);

    domain.DoCallBack(new CrossAppDomainDelegate(SetupLogging));
    return domain;
}

обновление:

кажется, что сборка загружена, если я смотрю в выход я вижу это

'TaskExecuter.Терминал.vshost.exe ' (управляемый (v4.0.30319)): Загружен 'NLog' 'TaskExecuter.Терминал.vshost.exe ' (управляемый (v4.0.30319)): загружен 'TaskExecuter', загружены символы.

но я все равно получаю исключение... я этого не понимаю!--4-->

Система.ИО.FileNotFoundException было необработанное сообщение=не удалось загрузить файл или сборка ' TaskExecuter, версия=1.0.4244.31921, Culture=neutral, PublicKeyToken=null' или одна из его зависимостей. Этот система не может найти указанный файл. Source=mscorlib
FileName=TaskExecuter, Version=1.0.4244.31921, культура=нейтральный, Значения PublicKeyToken=null в FusionLog==== предварительно привязать государственной информационной === Журнал: User = Peter-PCPeter LOG: DisplayName = TaskExecuter, Версия=1.0.4244.31921, культура=нейтральная, PublicKeyToken=null (Полностью указанный) журнал: Appbase = файл:///C:/ProgramData / TaskExecuter / TaskLib / uTorrentTasks журнал: начальный PrivatePath = C:ProgramDataTaskExecuterTaskLibuTorrentTasksLibs Вызов сборки: (неизвестно). === Журнал: данная привязка начинается в контексте загрузки по умолчанию. LOG: использование файл конфигурации приложения: d:userspeterdocumentsvisual студия 2010ПроектыTaskExecuterTaskExecuter.ТерминалbinReleaseTaskExecuter.Терминал.vshost.исполняемый.Конфиг Журнал: использование файла конфигурации хоста: журнал: использование конфигурации компьютера файл из C:WindowsMicrosoft.NETFrameworkv4.0.30319configmachine.config. LOG: политика не применяется к ссылке в настоящее время (частная, пользовательская, частичная или привязка сборки на основе местоположения). Журнал: попытка загрузка нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.файл DLL. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.файл DLL. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/библиотеки/TaskExecuter.файл DLL. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/библиотеки/TaskExecuter/TaskExecuter.файл DLL. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.ИСПОЛНЯЕМЫЙ. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.ИСПОЛНЯЕМЫЙ. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/библиотеки/TaskExecuter.ИСПОЛНЯЕМЫЙ. LOG: попытка загрузки нового URL файл:///с:/папке ProgramData./TaskExecuter/TaskLib/uTorrentTasks/библиотеки/TaskExecuter/TaskExecuter.ИСПОЛНЯЕМЫЙ.

трассировка стека: в системе.Отображение.RuntimeAssembly._nLoad (AssemblyName имя файла, Строковая кодовая база, assemblySecurity доказательств, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) в системе.Отображение.RuntimeAssembly.nLoad (AssemblyName имя файла, строковая кодовая база, Assemblysecurity доказательств, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) на Система.Отображение.RuntimeAssembly.InternalLoadAssemblyName (AssemblyName assemblyRef, Assemblysecurity доказательства, StackCrawlMark& stackMark, Boolean forIntrospection, Boolean suppressSecurityChecks) в системе.Отображение.RuntimeAssembly.InternalLoad (Строка assemblyString, Assemblysecurity доказательств, StackCrawlMark& stackMark, Логическое forIntrospection) в системе.Отображение.Собрание.Загрузить (строка assemblyString) на Система.Во время выполнения.Сериализация.FormatterServices.LoadAssemblyFromString (Строка имя_сборки) на Система.Отображение.MemberInfoSerializationHolder..ctor (SerializationInfo информация, StreamingContext контекстные) в системе.домен приложений.DoCallBack (CrossAppDomainDelegate callBackDelegate) в TaskExecuter.AppDomainHelper.CreateAppDomain (String dir, Имя строки) в d:userspeterdocumentsvisual студия 2010ПроектыTaskExecuterTaskExecuterAppDomainHelper.cs: линия 50 в TaskExecuter.TaskManagment.TaskFinder.Зонд() в d:userspeterdocumentsvisual студия 2010ПроектыTaskExecuterTaskExecuterTaskManagmentTaskFinder.cs: линия Двадцать девять в TaskExecuter.TaskManagment.Менеджер задач.LoadTasks () in d:userspeterdocumentsvisual студия 2010ПроектыTaskExecuterTaskExecuterTaskManagmentTaskManager.cs: линия Шестьдесят три в TaskExecuter.TaskManagment.Менеджер задач.Start () in d:userspeterdocumentsvisual студия 2010ПроектыTaskExecuterTaskExecuterTaskManagmentTaskManager.cs: линия Девяносто пять в TaskExecuter.Терминал.Программа.Main (String[] args) in d:userspeterdocumentsvisual студия 2010ПроектыTaskExecuterTaskExecuter.терминальная программа.cs: строка 16 в системе.домен приложений._nExecuteAssembly (сборка RuntimeAssembly, String [] args) в системе.домен приложений.ExecuteAssembly (String assemblyFile, Доказательство assemblySecurity, String[] args) на Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() в системе.Нарезка резьбы.ThreadHelper.ThreadStart_Context (Объект государство) в системе.Нарезка резьбы.Параллельном режиме.Выполнения(В Параллельном Режиме параллельном режиме, ContextCallback обратного вызова, состояние объекта, логическое ignoreSyncCtx) в системе.Нарезка резьбы.Параллельном режиме.Выполнения(В Параллельном Режиме параллельном режиме, ContextCallback обратного вызова, состояние объекта) в системе.Нарезка резьбы.ThreadHelper.ThreadStart()
Свойство innerexception:

6 ответов


я смог восстановить сообщение, связанное с блогом, используя archive.org а также придумать рабочее решение.

моя цель состояла в том, чтобы динамически скомпилировать exe во временное место, а затем загрузить тень exe все основные DLL в дочернем appdomain, чтобы основное приложение, которое породило exe, можно было легко обновить. Основным подходом является использование childAppDomain.CreateInstanceFrom для создания типа, который в конструкторе устанавливает обработчик событий разрешения сборки. Мой код выглядел как

var exportAppDomain = AppDomain.CreateDomain(
    runnerName,
    null,
    appDomainSetup,
    new PermissionSet(PermissionState.Unrestricted));

exportAppDomain.CreateInstanceFrom(
    Assembly.GetExecutingAssembly().Location,
    "ExportLauncher.AppDomainResolver",
    true,
    BindingFlags.Public | BindingFlags.Instance,
    null,
    new object[] { Assembly.GetExecutingAssembly().Location },
    null,
    null);

и тип, который создает необходимый обработчик AssemblyResolve (сообщение в блоге ниже описывает, почему вам нужен другой тип)

class AppDomainResolver
{
    string _sourceExeLocation;

    public AppDomainResolver(string sourceExeLocation)
    {
        _sourceExeLocation = sourceExeLocation;
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (args.Name.Contains("exporterLauncher")) // why does it not already know it has this assembly loaded? the seems to be required
            return typeof(AppDomainResolver).Assembly;
        else
            return null;
    }
}

вот оригинальный пост в блоге:

Домены приложений трудно...

вы когда-нибудь работали с доменом приложений в .NET?, в начале это не кажется таким уж трудным, но те, кого вы узнаете, вы начинаете понимать все мало трудности.

все работает нормально, пока вы не выходите за пределы Хоста AppDomains.BaseDirectory, но в нашем случае мы хотели, чтобы плагины были развернуты в месте "C:\My плагины" в то время как хост-приложение будет работать на "C:\Program Files\My App", так как мы могли бы столкнуться с зависимостями от AppDomain для некоторых проблем с узлом сборки, по-видимому, было неизбежно.

Классический Вот несколько простых кодов и наш первый попытка.

 1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);

кажется очень простым, но потому, что "ApplicationBase" отличается от " AppDomain.CurrentDomain.BaseDirectory" мы столкнулись с тем, что кажется очень хорошо знают исключений.

Система.ИО.FileNotFoundException: не удалось загрузить файл или узел сборки.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' или одна из его зависимостей. Система не может найти указанный файл.

если вы работали с какой-либо динамической загрузки я уверен, что вам это знакомо. И проблема в том, что " хозяин.Службы " были известны в домене хост-приложения, потому что он хранится в "C:\Program Files\My App", и домен приложения, который ищет его, смотрит в "C:\My Плагины".

Ну, мы думали, что мы поручили ему также посмотреть в " AppDomain.CurrentDomain.BaseDirectory " который был бы "C:\Program Files\My App", но это было не так.

AppDomain.AssemblyResolve на помощь? Итак мы работали с этими причудами раньше, поэтому мы знали, как мы могли бы использовать "AppDomain.AssemblyResolve " для ручного разрешения любых сборок, которые не удалось обработать AppDomain.

1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);
  13:  domain.AssemblyResolve += Resolve;

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

снова исключение выглядит очень похоже на предыдущее, но на этот раз оно не может найти сборку, содержащую тип, который имеет обработчик "Resolve", который мы настроили в самой последней строке в приведенном выше фрагменте.

AppDomain.Тогда заряжай! Итак, очевидно, что при подключении обработчика событий домен приложения должен знать тип объекта, обрабатывающего это событие, что на самом деле довольно понятно, когда вы думаете об этом, поэтому, если Домен приложения даже не может найти его и загрузить, мы ничего не можем обработать.

Итак, что дальше? Наша идея состояла в том, чтобы вручную поручить домену приложения загрузить неглубокую сборку, у которой не было никаких других зависимостей, которые можно было найти в GAC, и подключить обработчик событий.

 1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);
  13:  domain.Load(File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Host.AssemblyLoader.dll")));
  14:  domain.AssemblyResolve += new AssemblyLoader(AppDomain.CurrentDomain.BaseDirectory).Handle;

используя очень простой маленький класс, как показано ниже, и не возражайте против странного поведения разрешения.

 1:  [Serializable]
   2:  public class AssemblyLoader
   3:  {
   4:      private string ApplicationBase { get; set; }
   5:   
   6:      public AssemblyLoader(string applicationBase)
   7:      {
   8:          ApplicationBase = applicationBase;
   9:      }
  10:   
  11:      public Assembly Resolve(object sender, ResolveEventArgs args)
  12:      {
  13:          AssemblyName assemblyName = new AssemblyName(args.Name);
  14:          string fileName = string.Format("{0}.dll", assemblyName.Name);
  15:          return Assembly.LoadFile(Path.Combine(ApplicationBase, fileName));
  16:      }
  17:  }

так да или нет?- Нет!… такая же проблема до сих пор.

все гораздо проще! На самом деле все стало гораздо проще в конце, когда нам удалось заставить его работать.

Я не могу сказать, как именно команда .NET предполагала, что это должно работать, мы не могли действительно узнать какие-либо полезные вещи, для которых использовались "PrivateBinPath" и "PrivateBinPathProbe". Ну, мы используем их сейчас и заставили их работать так, как мы ожидали!

поэтому мы изменили класс "AssemblyLoader", чтобы он выглядел так:

   1:  [Serializable]
   2:  public class AssemblyLoader : MarshalByRefObject
   3:  {
   4:      private string ApplicationBase { get; set; }
   5:   
   6:      public AssemblyLoader()
   7:      {
   8:          ApplicationBase = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
   9:          AppDomain.CurrentDomain.AssemblyResolve += Resolve;
  10:      }
  11:   
  12:      private Assembly Resolve(object sender, ResolveEventArgs args)
  13:      {
  14:          AssemblyName assemblyName = new AssemblyName(args.Name);
  15:          string fileName = string.Format("{0}.dll", assemblyName.Name);
  16:          return Assembly.LoadFile(Path.Combine(ApplicationBase, fileName));
  17:      }
  18:  }

поэтому вместо того, чтобы подключать событие, в котором мы создали домен приложения, мы позволяем классу делать это самостоятельно и вместо этого "CurrentDomain".

Ok Итак, подождите, не вызывает ли это проблему при создании его на заводе, так как он теперь загружается для неправильного домена? Ну, к счастью, вы можете создавать объекты внутри домена извне.

таким образом, создание домена теперь делается следующим образом:

1:  string applicationBase = Path.GetDirectoryName(interOperabilityPackageType.AssemblyDescription.AssemblyPath);
   2:  AppDomainSetup setup = new AppDomainSetup
   3:  {
   4:      ApplicationName = name,
   5:      ApplicationBase = applicationBase,
   6:      PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
   7:      PrivateBinPathProbe = AppDomain.CurrentDomain.BaseDirectory,
   8:      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
   9:  };
  10:   
  11:  Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
  12:  AppDomain domain = AppDomain.CreateDomain(name, evidence, setup);
  13:  domain.CreateInstanceFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Host.AssemblyLoader.dll"),"Host.AssemblyLoader");

нам все равно для поддержания ссылки на" AssemblyLoader", поскольку он должен в значительной степени поддерживаться в живых, подключив его к событию.

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

выше по крайней мере, позволяет нам устанавливать плагины вдали от нашей базы хост-приложений, что я думаю, хорошо.

если кто-то решил это по-другому, то, пожалуйста, сделайте ответ, возможно, мы можем найти плюсы и минусы в любом случае, или просто найти лучшее решение.

если у вас есть какие-либо вопросы или вы не можете получить выше, чтобы работать, то не стесняйтесь спрашивать.

автор: Jens Melgaard / posted @ четверг, июль 01, 2010 3: 08 PM / обратная связь (0)


есть ли причина, по которой вы не используете исходные сборки ?

поэтому, если ваш внешний appdomain не использует учетные данные, которые мешают ему получить доступ к исходным сборкам, метод AppDomain.CreateInstanceFromAndUnwrap способен это сделать.

Я предлагаю вам изолировать удаленно выполняемый код в классе MarshalByRefObject, используя такой класс:

public class MyRemoteClass : MarshalByRefObject
{
    public void SetupLogging()
    { 
       // ...
    }
}

и используйте его так:

var assemblyPath = new Uri(typeof(MyRemoteClass).Assembly.CodeBase).LocalPath;
var remote = (MyRemoteClass)domain.CreateInstanceFromAndUnwrap(assemblyPath, "NameSpace.MyRemoteClass");

remote.SetupLogging();

Это позволит избежать ненужная проблема передачи возвращаемых значений через состояние appdomain, так как DoCallBack не возвращает значения. Это также позволит избежать смешивания кода сантехники AppDomain с логикой вашего приложения.

наконец, вам может потребоваться перехватить AppDomain.Однако AssemblyResolve внутри MyRemoteClass для правильной загрузки других зависимостей.


найдено решение после загрузки сборки из байтовой настройки .GetName ().Кодовая база для null решила проблему...

осмотревшись, я нашел этой страница, и у нее есть лучшее решение, чем мое!


согласно http://msdn.microsoft.com/en-us/library/aehss7y0.aspx поведение AppDomain.CreateDomain изменился .Серии net4 и вы должны использовать http://msdn.microsoft.com/en-us/library/ms130766.aspx и настройка Evidence и grants "вручную"...


Если вам нужно загрузить сборку самостоятельно, избегайте загрузки из байтов... Я бы рекомендовал использовать хотя бы загрузку по полному пути сборки.

В общем для изучения проблем с загрузкой сборок serach для "Fusion log viewer" (http://www.bing.com/search?q=fussion + log + viewer) и используйте инструмент, чтобы увидеть, откуда код пытается загрузить сборки.


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

система.ИО.FileNotFoundException было необработанное сообщение=не удалось загрузить файл или TaskExecuter сборку, версия=1.0.4244.31921, культура=нейтральных, PublicKeyToken=нуль' или одна из его зависимостей. Система не может найти указанный файл. Source=mscorlib

кроме того, что вы не можете загрузить свою сборку из другого места, чем в вашем ApplicationBase вероятно, какая-то сборка dependend отсутствует, откуда она может быть разрешена и загружена.

кстати, если вы начинаете загрузку с байтов, вы должны посмотреть на сборки, загруженные в ваш домен. Зависимая сборка может быть уже загружена, но зависимость не может быть решена автоматически. Если одна и та же сборка загружена дважды, ее типы будут несовместимы. Вы получите смешные CastExceptions говоря объект вашего класса не может быть брошен в ваш класс.

вы может попытаться зарегистрировать обработчик событий AssemblyResolve в вашем домене ,но с этим вы легко получите некоторые заклинания черной магии.dll ад. Если все остальное провалится и ты пойдешь .dll черт возьми, встретимся здесь: необходимо подключить AssemblyResolve событие, когда DisallowApplicationBaseProbing = true