AppDomain 执行程序集

Posted

技术标签:

【中文标题】AppDomain 执行程序集【英文标题】:AppDomain Execute Assembly 【发布时间】:2013-07-11 01:33:08 【问题描述】:

我正在尝试将程序集 (dll) 加载到 AppDomain 并调用入口点。 (基本上将包引导到 Azure 环境中)我一直在关注这篇 SO 文章 (How do I create an application domain and run my application in it?),我认为我做得对,但我遇到了一些问题。

我在这里使用了几篇文章来了解我目前的情况,但我一直遇到FileNotFoundException,如Unable to load executing assembly into new AppDomain, FileNotFoundException 中所述。我的问题是该解决方案不起作用。我尝试执行的程序集存在于不同的位置。所以ApplicationBase 需要是我要执行的程序集的文件夹。

var otherType = typeof(BootstrapProxy);
var domaininfo = new AppDomainSetup
    
        ConfigurationFile = executingAssembly + ".config",
        ApplicationBase = _root
    ;
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
_domain = AppDomain.CreateDomain(_type.ToString(), adevidence, domaininfo);
_domain.AssemblyResolve += (sender, args) =>
    
        var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        if (lookupPath == null) return null;
        var assemblyname = new AssemblyName(args.Name).Name;
        var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
        var assembly = Assembly.LoadFrom(assemblyFileName);
        return assembly;
    ;
var proxy = _domain.CreateInstanceAndUnwrap(otherType.Assembly.FullName, otherType.FullName) as BootstrapProxy;

最后一行引发异常,并且 AssemblyResolve 事件永远不会触发(通过在 var lookupPath 行上放置断点来确定。

我也尝试了AppDomain.CurrentDomain.AssemblyResolve 事件与上面相同的处理程序,但没有运气。我还尝试在 BootstrapProxy 类中创建相同的处理程序。

我认为我这样做是正确的,但请记下第一段,所以如果我完全偏离基础,我不反对以不同的方式做事。

更新:

我已经更改了代码以强制将程序集加载到新的 appdomain 中,但仍然存在问题。

var otherType = typeof(BootstrapProxy);
var domaininfo = new AppDomainSetup
    
        ConfigurationFile = executingAssembly + ".config",
        ApplicationBase = _root
    ;
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
_domain = AppDomain.CreateDomain(_type.ToString(), adevidence, domaininfo);
var deps = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var dep in deps)

    var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (lookupPath == null) continue;
    var assemblyname = new AssemblyName(dep.Name).Name;
    var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
    if (File.Exists(assemblyFileName))
        _domain.Load(File.ReadAllBytes(assemblyFileName));

_domain.Load(File.ReadAllBytes(Assembly.GetExecutingAssembly().Location));
var sl = _domain.GetAssemblies().ToArray();
var proxy = _domain.CreateInstanceAndUnwrap(otherType.Assembly.FullName, otherType.FullName) as BootstrapProxy;

sl 显示所有的 dll,包括在 FileNotFoundException 中引用的那个都加载到新的 appdomain 中。

public class BootstrapProxy : MarshalByRefObject

    public void Main()
    
        Console.WriteLine("Magic happened.");
    

更新 2:

我把它改成这样:

var deps = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var dep in deps)

    var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (lookupPath == null) continue;
    var assemblyname = new AssemblyName(dep.Name).Name;
    var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
    if (File.Exists(assemblyFileName))
        File.Copy(assemblyFileName, Path.Combine(_root, assemblyname + ".dll"));


File.Copy(Assembly.GetExecutingAssembly().Location, Path.Combine(_root, Path.GetFileName(Assembly.GetExecutingAssembly().Location)));
var otherType = typeof(BootstrapProxy);
var domaininfo = new AppDomainSetup
    
        ConfigurationFile = executingAssembly + ".config",
        ApplicationBase = _root
    ;
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
_domain = AppDomain.CreateDomain(_type.ToString(), adevidence, domaininfo);
var proxy = _domain.CreateInstanceAndUnwrap(otherType.Assembly.FullName, otherType.FullName) as BootstrapProxy;
if (proxy != null)

    proxy.Main();

这种复制程序集及其对新 AppDomain 的 ApplicationBase 的引用的方法并不理想,因为有一些常见的引用,我最终可能会遇到版本冲突和其他问题。

【问题讨论】:

【参考方案1】:

只是猜测,但问题就在这里:

var otherType = typeof(BootstrapProxy);

通过这样做,您将该程序集加载到调用应用程序域中。由于初始化,它会尝试加载调用域的查找路径中不存在的程序集。卡布姆!

解决这个问题:

通过其完全限定名称引用otherType,并将程序集名称也作为字符串传递。 (我认为您可能只使用该类型的 FQN 就可以逃脱)

还有。对于 appdomain 之外的程序集,您不应该真正处理 AssemblyResolve

【讨论】:

BootstrapProxy 存在于当前域中,因为它是当前正在执行的程序集,不是吗? @TheGreatCO:是的,而且由于它的加载方式,它不会导致AssemblyResolve 触发。 (我猜它存在于两个域中)。尝试硬编码字符串参数作为测试,它应该可以工作。 AssemblyResolve 甚至不需要。 @TheGreatCO:你似乎没有跟上。您不应该将任何内容加载到调用 appdomain 中(除非我误解了代码在做什么)。在此处查看示例:github.com/leppie/IronScheme/blob/master/IronScheme/IronScheme/… 我的问题是正在执行的程序集位于(例如)E:\AppRoot 上,而我希望执行的程序集位于 C:\Resources 中。所以(就像你一样)我将 AppDomain ApplicationBase 设置为 C:\Resources,但我正在尝试加载驻留在 E:\AppRoot 中的 Bootstrapper.dll。所以当前执行域和新域的应用基础并不相同,甚至不相近。 @TheGreatCO:我看到你在调用域中使用BootstrapProxy。该类是如何定义的?你需要它的目的是什么?

以上是关于AppDomain 执行程序集的主要内容,如果未能解决你的问题,请参考以下文章

AppDomain.Execute 程序集卡住

C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载

将多个程序集版本加载到多个 AppDomain 中

AppDomain 程序集加载导致致命的执行引擎错误 (6B3979C6) (80131506)

C# 通过 AppDomain 应用程序域实现程序集动态卸载或加载

添加当前 AppDomain 的路径