将程序集加载到另一个 AppDomain 和类型检查

Posted

技术标签:

【中文标题】将程序集加载到另一个 AppDomain 和类型检查【英文标题】:Loading Assemblies into another AppDomain and Type Inspection 【发布时间】:2012-11-30 03:17:30 【问题描述】:

我正在尝试将程序集加载到另一个应用程序域(以便在不需要时能够卸载它们),但我还希望能够检查加载的程序集中的类型并创建我的类型的实例(通过 Activator)。另外我不希望程序集在加载时被锁定。

我不明白这怎么可能。我尝试过的事情:

Loading Assembly.Load(File.ReadAllBytes(path)) 帮助我加载程序集而不锁定它,因此它可以被删除/移动。这会将程序集加载到当前的 appdomain 中,因此无法卸载它。

创建另一个 AppDomain 并使用 AppDomain.Load() 将程序集加载到新的 AppDomain 中,但无法检查加载的 AppDomain 中的所有类型。除非我知道类型完全限定的类型名称,否则我无法创建任何东西,即使那样它们也必须是可序列化的或从 MarshallByRef 派生的。另一个 AppDomain 东西只能通过代理工作,因此如果没有合同/共享接口等,就很难创建东西。

MEF 也将程序集加载到当前的 AppDomain 中,所以它基本上是在做同样的事情。

Mono.Cecil 允许对加载的程序集进行类型检查,但是我无法使用 TypeReference 创建类型。也许有办法将 TypeReference 转换为 Type?

我已经检查了 ILSpy 是如何做到这一点的,毫无疑问它使用 Mono.Cecil 加载/卸载程序集,但话又说回来,它不会创建任何类型的实例,只是进行可能通过的类型检查Mono.Cecil 路由。

现在的问题是,这可能吗?我错过了什么吗?

【问题讨论】:

【参考方案1】:

如果您将程序集加载到另一个应用程序域,那么您当然应该在那里创建实例并在那里使用它们。除此之外,不知道你遇到了什么问题。

更新

    添加了从列表中选择类型的代码 更改为仅将类型名称作为字符串跨域传递

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;

public class MainClass : MarshalByRefObject

    static void Main(string[] args)
    
        if (args.Length == 0)
        
            Console.WriteLine("usage: 0 assembly", Path.GetFileName(Assembly.GetExecutingAssembly().Location));
            return;
        
        AppDomain other = AppDomain.CreateDomain("other");
        Type myType = typeof(MainClass);
        // create a loader instance in the new domain
        MainClass loader = (MainClass)other.CreateInstanceAndUnwrap(myType.Assembly.FullName, myType.FullName);
        string assemblyName;
        string[] types = loader.LoadAssembly(args[0], out assemblyName);
        Console.WriteLine("Loaded assembly 0.", assemblyName);
        Console.WriteLine("Types:");
        for(int i = 0; i < types.Length; i += 1)
        
            Console.WriteLine("[0] 1", i, types[i]);
        
        Console.Write("Enter index of type to create, -1 to exit: ");
        int create = Int32.Parse(Console.ReadLine());
        if (create < 0)
        
            return;
        
        Console.WriteLine("Creating instance of type 0", types[create]);
        Console.WriteLine("Type of the created instance was: 0", loader.CreateInstance(assemblyName, types[create]));
    

    string[] LoadAssembly(string path, out string assemblyName)
    
        Console.WriteLine("LoadAssembly executing in appdomain 0", AppDomain.CurrentDomain.FriendlyName);
        Assembly assembly = Assembly.Load(File.ReadAllBytes(path));
        assemblyName = assembly.FullName;
        return Array.ConvertAll<Type, string>(assembly.GetExportedTypes(), x => x.FullName);
    

    string CreateInstance(string assemblyName, string typeName)
    
        Console.WriteLine("CreateInstance executing in appdomain 0", AppDomain.CurrentDomain.FriendlyName);
        object instance = Activator.CreateInstance(assemblyName, typeName).Unwrap();
        // do something useful with the instance here
        return instance.GetType().FullName;
    

    public override object InitializeLifetimeService()
    
        return null;
    

log4net.dll 的示例输出:

$ mono main.exe log4net.dll
LoadAssembly executing in appdomain other
Loaded assembly log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=a5715cc6d5c3540b.
Types:
[0] log4net.Core.SecurityContext
[1] log4net.Core.LoggerWrapperImpl
[2] log4net.Core.LogImpl
[3] log4net.Core.DefaultRepositorySelector
...
[160] log4net.Appender.AspNetTraceAppender
[161] log4net.Appender.FileAppender
...
[197] log4net.Repository.LoggerRepositoryConfigurationResetEventHandler
[198] log4net.Repository.LoggerRepositoryConfigurationChangedEventHandler
Enter index of type to create, -1 to exit: 161
Creating instance of type log4net.Appender.FileAppender
CreateInstance executing in appdomain other
Type of the created instance was: log4net.Appender.FileAppender

【讨论】:

将它指向第 3 方程序集,我得到一个 FileNotFoundException(即使文件在那里)。问题是我不想加载包含任何特定类型的程序集,而是想加载它们,并检查可用类型并根据某些标准创建一个/一些。加载的程序集可以是任何东西(从任何第 3 方程序集中选择)。此外,正在加载的程序集可以位于硬盘驱动器上的任何位置,而不是任何特定于应用程序的文件夹中。 在这里工作正常,我扔给它的任何东西都可以。 不能说我可以。我可以看到程序集被加载到另一个 AppDomain 中,并且正在执行 LoadAssembly 方法以查找所有类型,但是类型没有返回到主 AppDomain 并且我得到 FileNotFoundException,原因是程序集仅在第二个中加载AppDomain 和第一个不存在。 只有Type 跨域编组,因此您不需要加载程序集。我已经更新了答案,显示它在这里有效。您可以尝试在 string 数组中返回类型名称,这当然可以。 感谢 Jester,在字符串中编组类型名称使其工作,只是我无法创建对象实例并将它们传递回第一个 AppDomain(这听起来也合乎逻辑,因为仅加载了程序集在第二个 AppDomain 中)。不过我会接受你的解决方案。

以上是关于将程序集加载到另一个 AppDomain 和类型检查的主要内容,如果未能解决你的问题,请参考以下文章

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

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

无法将泛型类型加载到 ASP.NET 应用程序中的另一个 AppDomain

从 App.Config 将程序集加载到 AppDomain

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

将程序集加载到单独的 AppDomain 中,得到 InvalidCastException