当前位置:  开发笔记 > 编程语言 > 正文

如何以递归方式将所有引用加载到AppDomain的程序集?

如何解决《如何以递归方式将所有引用加载到AppDomain的程序集?》经验,为你挑选了6个好方法。

我想加载到一个新的AppDomain一些具有复杂引用树的程序集(MyDll.dll - > Microsoft.Office.Interop.Excel.dll - > Microsoft.Vbe.Interop.dll - > Office.dll - > stdole.dll)

据我所知,当加载程序集时AppDomain,它的引用不会自动加载,我必须手动加载它们.所以当我这样做时:

string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");

AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);

domain.Load(AssemblyName.GetAssemblyName(path));

得到了FileNotFoundException:

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

我认为关键部分是其依赖性之一.

好的,我之前做过 domain.Load(AssemblyName.GetAssemblyName(path));

foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
    domain.Load(refAsmName);
}

FileNotFoundException又一次,在另一个(参考)集会上.

如何递归加载所有引用?

在加载根程序集之前是否必须创建引用树?如何在不加载程序集的情况下获取程序集的引用?



1> Jduv..:

您需要CreateInstanceAndUnwrap在代理对象在外部应用程序域中执行之前调用.

 class Program
{
    static void Main(string[] args)
    {
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);

        var assembly = value.GetAssembly(args[0]);
        // AppDomain.Unload(domain);
    }
}

public class Proxy : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFile(assemblyPath);
        }
        catch (Exception)
        {
            return null;
            // throw new InvalidOperationException(ex);
        }
    }
}

另请注意,如果您使用,LoadFrom您可能会遇到FileNotFound异常,因为程序集解析程序将尝试查找您在GAC或当前应用程序的bin文件夹中加载的程序集.使用LoadFile加载任意组件文件,而不是-但请注意,如果你这样做,你需要自己加载任何依赖关系.


看看我写的代码来解决这个问题:https://github.com/jduv/AppDomainToolkit.具体来说,请查看此类中的LoadAssemblyWithReferences方法:https://github.com/jduv/AppDomainToolkit/blob/master/AppDomainToolkit/AppDomainContext.cs
我发现虽然这个**大多数时候都有效,但在*某些情况下,你实际上仍然需要将一个处理程序附加到`AppDomain.CurrentDomain.AssemblyResolve`事件,如[此MSDN答案]中所述(http:/ /social.msdn.microsoft.com/Forums/en-US/0a18ed66-6995-4e7c-baab-61c1e528fb82/why-does-appdomaincreateinstanceandunwrap-work-and-appdomaincreateinstancefromunwrap).在我的情况下,我试图挂钩在MSTest下运行的SpecRun部署,但我认为它适用于许多情况,其中您的代码可能无法从"主"AppDomain - VS扩展,MSTest等运行.
@Jduv你确定`assembly`变量会引用"MyDomain"中的程序集吗?我想通过`var assembly = value.GetAssembly(args [0]);`你将把`args [0]`加载到两个域中,`assembly`变量将引用来自主应用程序域的副本

2> 小智..:

http://support.microsoft.com/kb/837908/en-us

C#版本:

创建一个主持人类并从MarshalByRefObject以下位置继承它:

class ProxyDomain : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFrom(assemblyPath);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(ex.Message);
        }
    }
}

来自客户站点的电话

ProxyDomain pd = new ProxyDomain();
Assembly assembly = pd.GetAssembly(assemblyFilePath);


这完全是胡说八道.继承自"MarshalByRefObject"并没有神奇地使它加载到其他所有的"AppDomain"中,它只是告诉.NET框架创建透明的远程处理代理,而不是在从另一个AppDomain中的一个`AppDomain`中解包引用时使用序列化`(典型的方式是`CreateInstanceAndUnwrap`方法).不敢相信这个答案有超过30个赞成票; 这里的代码只是一种无谓的迂回方式调用`Assembly.LoadFrom`.
这不起作用.如果您执行代码并检查AppDomain.CurrentDomain.GetAssemblies(),您将看到您尝试加载的目标程序集被加载到*current*应用程序域中而**不是代理程序域.
如何将此解决方案置于创建新AppDomain的上下文中,有人可以解释一下吗?
可以在appdomains周围传递`MarshalByRefObject`.所以我猜想`Assembly.LoadFrom`试图在一个新的appdomain中加载程序集,如果调用对象可以在这些appdomains之间传递,那么这是唯一可行的.这也称为远程处理,如下所述:http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject%28v=vs.80%29.aspx

3> David..:

在新的AppDomain上,尝试设置AssemblyResolve事件处理程序.缺少依赖项时会调用该事件.



4> Nir..:

一旦将程序集实例传递回调用者域,调用者域将尝试加载它!这就是你得到例外的原因.这发生在你的最后一行代码中:

domain.Load(AssemblyName.GetAssemblyName(path));

因此,无论你想对程序集做什么,都应该在代理类中完成 - 一个继承MarshalByRefObject的类.

记住调用者域和新创建的域应该都可以访问代理类程序集.如果您的问题不是太复杂,请考虑保持ApplicationBase文件夹不变,因此它将与调用者域文件夹相同(新域只会加载它需要的程序集).

在简单的代码中:

public void DoStuffInOtherDomain()
{
    const string assemblyPath = @"[AsmPath]";
    var newDomain = AppDomain.CreateDomain("newDomain");
    var asmLoaderProxy = (ProxyDomain)newDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(ProxyDomain).FullName);

    asmLoaderProxy.GetAssembly(assemblyPath);
}

class ProxyDomain : MarshalByRefObject
{
    public void GetAssembly(string AssemblyPath)
    {
        try
        {
            Assembly.LoadFrom(AssemblyPath);
            //If you want to do anything further to that assembly, you need to do it here.
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(ex.Message, ex);
        }
    }
}

如果确实需要从与当前应用程序域文件夹不同的文件夹加载程序集,请使用特定的dll搜索路径文件夹创建新的应用程序域.

例如,上面代码中的app域创建行应替换为:

var dllsSearchPath = @"[dlls search path for new app domain]";
AppDomain newDomain = AppDomain.CreateDomain("newDomain", new Evidence(), dllsSearchPath, "", true);

这样,所有dll都将自动从dllsSearchPath中解析出来.



5> Dustin Campb..:

如果引用的程序集不在GAC中或在CLR的探测路径上,则需要处理AppDomain.AssemblyResolve或AppDomain.ReflectionOnlyAssemblyResolve事件(取决于您正在执行的负载).

AppDomain.AssemblyResolve

AppDomain.ReflectionOnlyAssemblyResolve



6> grouma..:

我花了一段时间才了解@ user1996230的答案,因此我决定提供一个更明确的示例。在下面的示例中,我为另一个AppDomain中加载的对象创建代理,并从另一个域对该对象调用方法。

class ProxyObject : MarshalByRefObject
{
    private Type _type;
    private Object _object;

    public void InstantiateObject(string AssemblyPath, string typeName, object[] args)
    {
        assembly = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + AssemblyPath); //LoadFrom loads dependent DLLs (assuming they are in the app domain's base directory
        _type = assembly.GetType(typeName);
        _object = Activator.CreateInstance(_type, args); ;
    }

    public void InvokeMethod(string methodName, object[] args)
    {
        var methodinfo = _type.GetMethod(methodName);
        methodinfo.Invoke(_object, args);
    }
}

static void Main(string[] args)
{
    AppDomainSetup setup = new AppDomainSetup();
    setup.ApplicationBase = @"SomePathWithDLLs";
    AppDomain domain = AppDomain.CreateDomain("MyDomain", null, setup);
    ProxyObject proxyObject = (ProxyObject)domain.CreateInstanceFromAndUnwrap(typeof(ProxyObject).Assembly.Location,"ProxyObject");
    proxyObject.InstantiateObject("SomeDLL","SomeType", new object[] { "someArgs});
    proxyObject.InvokeMethod("foo",new object[] { "bar"});
}

推荐阅读
惬听风吟jyy_802
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有