自定义 AppDomain 和 PrivateBinPath

Posted

技术标签:

【中文标题】自定义 AppDomain 和 PrivateBinPath【英文标题】:Custom AppDomain and PrivateBinPath 【发布时间】:2011-07-08 15:21:56 【问题描述】:

我使用 c# 4.0 和一个控制台应用程序仅用于测试,以下代码确实给出了异常。

AppDomainSetup appSetup = new AppDomainSetup()

    ApplicationName = "PluginsDomain",
    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
    PrivateBinPath = @"Plugins",
    ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
;

AppDomain appDomain = AppDomain.CreateDomain("PluginsDomain", null, appSetup);

AssemblyName assemblyName = AssemblyName.GetAssemblyName(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins", "sample.dll"));

Assembly assembly = appDomain.Load(assemblyName); //This gives an exception of File not found

AppDomain.Unload(appDomain);

在我创建的 AppDomain 上使用 Load 时,我不断收到 File not found 异常。

谢谢。

【问题讨论】:

试试看这里:***.com/q/658498/735864 @danyolgiax,那里提供的解决方案没有使用新创建的应用程序域,他只是使用了 Assembly.LoadFrom 我猜它会将它加载到当前的应用程序域中,这不是我想要的,除非我错了关于某事,无论如何。 【参考方案1】:

首先确保插件是AppDomain 基本路径的子目录。 PrivateBinPath 仅适用于 here 所述的子目录

如果这不是问题,请查看您的融合绑定日志。使用fusion log viewer 上面还有一个很好的Blog Post。融合日志将告诉您它在哪里搜索程序集。这应该会告诉您您的路径是否包含在搜索中。

另一种可能性是它正在查找您的程序集,但不是它的依赖项之一。融合日志查看器会再次告诉您。

【讨论】:

感谢您的宝贵时间,Plugins 文件夹是应用程序域基目录的子目录,但我想通了,如果我将 dll 复制到应用程序基目录,它可以工作,它不起作用当它位于“Plugins”文件夹的私有 bin 路径中时。当我使用融合日志时,它似乎没有在私有 bin 路径中搜索,你知道为什么吗? “PrivateBinPath 只适用于子目录” - 这就是这该死的东西不起作用的原因。谢谢谢谢谢谢。 链接来自:PrivateBinPath 将仅适用于所描述的子目录不再起作用【参考方案2】:

我在尝试从 bin 目录之外的目录动态加载 dll 文件时遇到了这个线程。长话短说,我能够通过使用AppDomain.CurrentDomain.AssemblyResolve 事件来实现这一点。代码如下:

//--begin example:

public MyClass()
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;


private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)

    foreach (var moduleDir in _moduleDirectories)
    
        var di = new DirectoryInfo(moduleDir);
        var module = di.GetFiles().FirstOrDefault(i => i.Name == args.Name+".dll");
        if (module != null)
        
            return Assembly.LoadFrom(module.FullName);
        
    
    return null;


//---end example

每次调用AppDomain.CurrentDomain.Load("...") 方法时都会调用CurrentDomain_AssemblyResolve 方法。此自定义事件处理程序使用您自己的自定义逻辑执行定位程序集的工作(这意味着您可以告诉它查看任何地方,甚至在 bin 路径之外等)。我希望这可以节省其他人几个小时...

【讨论】:

上面的代码不能正常工作,因为args.Name 是所需程序集的完全限定名称,i.Name 将为您提供文件名。当您从上面复制粘贴(并在您的代码中对其进行修补)时,请记住这一点。最简单的方法可能是匹配args.Name.Split(',')[0] + ".dll"【参考方案3】:

我想我已经弄清楚为什么会发生这种情况,那是因为即使您在不同的应用程序域中加载程序集,当前域也需要加载程序集,当前域需要知道它并加载它,就是这样因为 .NET 是如何设计的。

查看这里了解详情。

http://msdn.microsoft.com/en-us/library/36az8x58.aspx

当我检查融合日志时,我发现新创建的应用域能够成功地从私有bin路径加载程序集,以及为什么你仍然得到“找不到文件”异常的原因,因为这个异常原本属于当前应用域。

这意味着如果您将程序集复制到当前应用程序路径或当前域正在探测的路径中,您会发现可以将程序集加载到您的自定义域中。

希望对您有所帮助。

【讨论】:

"此方法仅用于将程序集加载到当前应用程序域中。提供此方法是为了方便无法调用静态 Assembly.Load 方法的互操作调用者。"你这样做是错的。您应该跨 AD 屏障注入一个类,并让该类使用 Assembly.Load 或 Assembly.LoadFrom 加载程序集。您不妨完全放弃另一个 appdomain,因为您没有将程序集与正在执行的 AppDomain 隔离开来。 威尔是对的。您应该调用 appDomain.CreateInstanceAndUnwrap 之类的东西来创建一个从新域上的 MarshalByRefObject 继承的对象并返回一个代理。然后您可以使用该代理,就好像它是在您的主域上创建的普通对象一样。如果该对象具有LoadTheNecessaryAssemblies 方法,如果您从主域上的代理调用该方法,因为该方法将真正在您的新域上运行,您将在新域中加载必要的程序集。跨度> Ahmed 的回答很准确,阅读文档会产生奇迹。我不同意@Will 的观点“您应该注入一个类”,因为有时即使您已成功“注入”(又名“在目标应用程序域中创建类型的实例”),有时仍需要程序集解析处理程序,因为某些程序集驻留在私人 bin 文件夹中没有解析(如 Plugins 文件夹。)也就是说,原始代码的真正修复是指定完整路径,程序集解析处理程序应该不是必需的。在这些场景中,融合日志是福音。 @ShaunWilson 加载程序集的需要并不能否定我所说的任何内容。我的观点是,如果您使用 AppDomains 来隔离代码,您不应该(也绝对不必)将该代码加载到当前 AppDomain 中。 “[B]因为即使您在不同的应用程序域中加载程序集,当前域也需要加载程序集......”这句话是完全错误的。 啊,我明白了,所以“您可以改用Activator.CreateInstance 在另一个应用程序域中创建目标类型的实例,只要您不尝试解开返回的对象您不需要在远程边界上复制程序集的句柄"

以上是关于自定义 AppDomain 和 PrivateBinPath的主要内容,如果未能解决你的问题,请参考以下文章

跨 AppDomain 传递自定义对象

在 C# 中的自定义 appdomain 中加载 dll

列出存储在 AppDomain 中的所有自定义数据

在不知道类型的情况下将程序集从文件加载到自定义 AppDomain

将代理对象克隆到当前 AppDomain

受限 AppDomain 中的 C# 类继承自位于主 AppDomain 中的其他类