将程序集加载到另一个AppDomain中并进行类型检查

4
我正在尝试将程序集加载到另一个应用程序域中(以便在不需要时卸载它们),但我也希望能够检查已加载程序集中的类型并创建我的类型的实例(通过Activator)。此外,我不希望在加载时锁定这些程序集。
我不知道这是否可能。我尝试过以下几种方法:
- 使用Assembly.Load(File.ReadAllBytes(path))可帮助我加载程序集而不会锁定它,因此可以删除/移动它。但是,这会将程序集加载到当前应用程序域中,因此无法卸载它。 - 创建另一个AppDomain并使用AppDomain.Load()将程序集加载到新的AppDomain中,但无法检查已加载AppDomain中的所有类型。除非我知道完全限定的类型名称,否则我无法创建任何内容,即使知道名称,也必须是可序列化的或派生自MarshallByRef。另一个AppDomain的东西只能通过代理工作,因此更难创建没有合同/共享接口等的内容。 - MEF也将程序集加载到当前应用程序域中,因此基本上是在做相同的事情。 - Mono.Cecil允许对已加载程序集进行类型检查,但是我不能使用TypeReference创建类型。也许如果有一种方式可以将TypeReference转换为Type?
我已经检查了ILSpy如何执行此操作,并且毫不意外地使用Mono.Cecil来加载/卸载程序集,但是它不会创建任何类型的实例,只是通过Mono.Cecil路由执行类型检查。
现在的问题是,这是否可能?我有什么遗漏的吗?
1个回答

1

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

更新

  1. 添加了从列表中选择类型的代码
  2. 更改为仅通过字符串传递类型名称跨域

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

将其指向第三方程序集时,我会收到FileNotFoundException(即使文件确实存在)。问题在于我不想加载包含任何特定类型的程序集,而是想要加载它们,并检查可用类型,并根据某些标准创建一个/多个类型。加载的程序集可以是任何东西(从任何第三方程序集中选择)。另外,正在加载的程序集可以位于硬盘上的任何位置,而不是任何应用程序特定文件夹中。 - Hadi Eskandari
在这里运行良好,无论我投入什么都可以。 - Jester
谢谢Jester,将类型名称编组为字符串使其正常工作,只是我无法创建对象实例并将它们传回第一个AppDomain(这也听起来很合理,因为程序集仅在第二个AppDomain中加载)。尽管如此,我会接受你的解决方案。 - Hadi Eskandari
请注意,Assembly.Load在某些情况下可能不足够。例如,如果您正在加载一个名为A的程序集,它既不位于基本目录中,也不位于MONO_PATH中,并且它引用了同一目录中的另一个名为B的程序集,则Mono将无法解析任何类型从程序集A引用了公共成员中的程序集B的类型。另一方面,Assembly.LoadFrom将在LoadFrom上下文中加载程序集,然后Mono将能够加载程序集B。 - Panos Rontogiannis
请注意,Assembly.LoadFrom可能会导致其他问题,通常除了特殊情况外不建议使用,其中最常见的是加载插件。无论如何,您应该准备好从Assembly.GetExportedTypes抛出的异常。 - Panos Rontogiannis
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接