将当前程序集加载到不同的AppDomain中

Pet*_*ter 9 .net c# appdomain

我创建了AppDomain一个不同的基目录.但是,我似乎无法将当前正在执行的程序集加载到其他AppDomain中,而没有基本目录中当前正在执行的程序集的副本.我甚至试图从字节加载它.

我尝试加载时没有异常,但是当我尝试使用时:

domain.DoCallBack(new CrossAppDomainDelegate(... 
Run Code Online (Sandbox Code Playgroud)

我明白了:

无法加载文件或程序集............系统找不到指定的文件.

我的代码如下:

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;
}
Run Code Online (Sandbox Code Playgroud)

更新:

如果我查看输出,看起来组件已加载我看到了这一点

'TaskExecuter.Terminal.vshost.exe'(Managed(v4.0.30319)):已加载'NLog''TaskExecuter.Terminal.vshost.exe'(托管(v4.0.30319)):已加载'TaskExecuter',已加载符号.

但我仍然得到例外......我不明白这一点

System.IO.FileNotFoundException未处理Message =无法加载文件或程序集'TaskExecuter,Version = 1.0.4244.31921,Culture = neutral,PublicKeyToken = null'或其依赖项之一.该系统找不到指定的文件.Source = mscorlib
FileName = TaskExecuter,Version = 1.0.4244.31921,Culture = neutral,PublicKeyToken = null FusionLog ====预绑定状态信息=== LOG:User = Peter-PC\Peter LOG:DisplayName = TaskExecuter,Version = 1.0.4244.31921文化=中性公钥=空(完全指定的)LOG:应用平台=文件:/// C:/ ProgramData/TaskExecuter/taskLib内/ uTorrentTasks LOG:初始PrivatePath = C:\ ProgramData\TaskExecuter\taskLib内\ uTorrentTasks\Libs调用程序集:(未知).===日志:此绑定在默认加载上下文中启动.日志:使用应用程序配置文件:d:\ users\peter\documents\visual studio 2010\Projects\TaskExecuter\TaskExecuter.Terminal\bin\Release\TaskExecuter.Terminal.vshost.exe.Config LOG:使用主机配置文件:LOG:使用C:\ Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config中的计算机配置文件.日志:此时策略未应用于引用(私有,自定义,部分或基于位置的程序集绑定).日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.DLL.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.DLL.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.DLL.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.DLL.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter.EXE.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/TaskExecuter/TaskExecuter.EXE.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter.EXE.日志:尝试下载新的URL文件:/// C:/ProgramData/TaskExecuter/TaskLib/uTorrentTasks/Libs/TaskExecuter/TaskExecuter.EXE.

StackTrace:System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName,String codeBase,System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName,String codeBase,Evidence assemblySecurity,RuntimeAssembly locationHint,StackCrawlMark&stackMark,Boolean throwOnFileNotFound,Boolean forIntrospection,Boolean suppressSecurityChecks))证据assemblySecurity,RuntimeAssembly locationHint,StackCrawlMark&stackMark,Boolean throwOnFileNotFound,Boolean forIntrospection,Boolean suppressSecurityChecks)System.Reflection.RuntimeAssembly.InternalLoad的System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef,Evidence assemblySecurity,StackCrawlMark&stackMark,Boolean forIntrospection,Boolean suppressSecurityChecks) (String assemblyString,Evidence assemblySecurity,StackCrawlMark&stackMark,Boolean forIntrospection)在System.Runtime.Serialization.FormatterServices.LoadAssemblyFromSt上的System.Reflection.Assembly.Load(String assemblyString)处.在System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)在TaskExecuter.AppDomainHelper.CreateAppDomain(字符串目录,字符串名称)在d环(字符串的AssemblyName)在System.Reflection.MemberInfoSerializationHolder..ctor(SerializationInfo中的信息,的StreamingContext上下文):\用户\彼得\文件\视觉工作室2010 \项目\ TaskExecuter\TaskExecuter\AppDomainHelper.cs:管线50以TaskExecuter.TaskManagment.TaskFinder.Probe()中d:\用户\彼得\文件\视觉工作室2010 \项目\ TaskExecuter\TaskExecuter\TaskManagment\TaskFinder.cs:在TaskExecuter.TaskManagment.TaskManager.LoadTasks(d)中第29行:\ USERS \彼得\文档\ Visual Studio 2010的\项目\ TaskExecuter\TaskExecuter\TaskManagment\TaskManager.cs:行63 TaskExecuter.TaskManagment d:\ users\peter\documents\visual studio 2010中的.TaskManager.Start()\ Projects\TaskExecuter\TaskExecuter\TaskManagment\TaskManager.cs:d中的TaskExecuter.Terminal.Program.Main(String [] args)中的第95行:\ users\peter\documents\visual studio 2010\Proj 学分\ TaskExecuter\TaskExecuter.Terminal\Program.cs的:在System.AppDomain._nExecuteAssembly(RuntimeAssembly组件,字串[] args)在System.AppDomain.ExecuteAssembly(字符串assemblyFile,证据assemblySecurity,字串[] args)在微软线16. VisualStudio.HostingProcess.HostProc.RunUsersAssembly()在System.Threading.ThreadHelper.ThreadStart_Context在System.Threading.ExecutionContext.Run(的ExecutionContext的ExecutionContext,ContextCallback回调,对象的状态,布尔ignoreSyncCtx)在System.Threading.ExecutionContext.Run(对象状态) (ExecutionContext executionContext,ContextCallback回调,对象状态)在System.Threading.ThreadHelper.ThreadStart()
InnerException:

Bra*_*AGr 9

我能够使用archive.org恢复链接到博客文章,并提出了一个可行的解决方案.

我的目标是动态地将exe编译到临时位置,然后让exe阴影加载子appdomain中的所有主dll,以便可以轻松更新生成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);
Run Code Online (Sandbox Code Playgroud)

以及创建所需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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是原始博文:

应用域很难......

你有没有在.NET中使用Application Domain?一开始看起来并不那么困难,但是你了解它们就会开始意识到所有的困难.

只要您不移动到Host AppDomains.BaseDirectory之外,一切正常,但在我们的情况下,我们希望在主机应用程序运行的"C:\ My Plug-ins"位置部署插件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);
Run Code Online (Sandbox Code Playgroud)

看起来很简单,但因为"ApplicationBase"与"AppDomain.CurrentDomain.BaseDirectory"不同,我们遇到了一个看似非常熟悉的异常.

System.IO.FileNotFoundException:无法加载文件或程序集"Host.Services,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null"或其依赖项之一.该系统找不到指定的文件.

如果您使用过任何类型的动态加载程序集,我相信您很熟悉.问题是"Host.Services"在Host Application Domain中是已知的,因为它存储在"C:\ Program Files\My App"中,而寻找它的Application Domain正在查看"C:\ My Plug-插件".

好吧,我们以为我们指示它也会查看"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;
Run Code Online (Sandbox Code Playgroud)

这应该是正确的,我们是这么认为的,而我们又错了,现在发生的事情是,实际上不是初始化应用程序域并使用它,而是在我们连接事件处理程序以解析程序集的地方失败了.

同样,异常看起来非常像前面提到的,但是这次它找不到包含Type的程序集,它包含我们在上面代码片段的最后一行中设置的"Resolve"处理程序.

AppDomain.Load然后!好吧,很明显在连接事件处理程序时,Application Domain需要知道处理该事件的对象的类型,当你考虑它时实际上是可以理解的,所以如果Application Domain甚至找不到那个,加载我们无法真正处理任何事情.

接下来是什么?我们的想法是手动指示Application Domain加载一个浅组件,该组件没有任何其他依赖关系,可以在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;
Run Code Online (Sandbox Code Playgroud)

使用一个非常简单的小类,如下所示,并不介意奇怪的Resolve行为.

 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:  }
Run Code Online (Sandbox Code Playgroud)

那么是或否?...不!......同样的问题仍然存在.

事情要简单得多!实际上,当我们设法使其工作时,事情变得更加简单.

我不能说.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:  }
Run Code Online (Sandbox Code Playgroud)

因此,我们不是将我们创建应用程序域的事件挂钩,而是让它自己完成它,而不是"CurrentDomain".

好吧等等,这不会导致在工厂中创建它时出现问题,因为它现在正在加载错误的域名吗?谢天谢地,您可以从外部在域内创建对象.

因此,现在按如下方式创建域:

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");
Run Code Online (Sandbox Code Playgroud)

我们甚至不关心维护对"AssemblyLoader"的引用,因为它应该通过将它自己挂钩到事件来保持活着.

希望这可以帮助那些偶然发现同样问题的人,我看到很多解决方法,然后人们要么只是让插件安装在同一个主机目录中,并且随着插件一起部署了所有必要的依赖项,即使它不是'插件知道它依赖于某些东西等等.

以上至少使我们能够从我们认为很好的主机应用程序库安装插件.

如果有人以不同的方式解决了这个问题,那么请回复,也许我们可以找到任何方式的优点和缺点,或者只是找到更好的解决方案.

如果您有任何疑问或无法完成上述工作,请随时提出.

作者:Jens Melgaard | 发表于@ Thursday,July 01,2010 3:08 PM | 反馈(0)

  • PrivateBinPathProbe并不是一个目录,而是一种指示应该探测privatebinpath的方法.你应该像bool一样使用它.http://msdn.microsoft.com/en-us/library/system.appdomainsetup.privatebinpathprobe%28v=vs.110%29.aspx (2认同)