在新的应用程序域中加载程序集

Posted

技术标签:

【中文标题】在新的应用程序域中加载程序集【英文标题】:Load assembly in new appdomain 【发布时间】:2020-09-02 14:53:29 【问题描述】:

我正在开发需要不同程序集的服务,但在编译时我们不知道哪些程序集。因此我想动态加载卸载不同的程序集。 我已经使用此代码进行加载,

static private void ExecuteAssemblyMethod(string taskName, string className, string method)
    
        //load assembly in parent application domain, the assembly can not be unloaded.
        var task = Assembly.LoadFrom($"tasks\\taskName.exe");
        var myType = task.GetType($"taskName.className");
        MethodInfo myMethod = myType.GetMethod(method);
        object obj = Activator.CreateInstance(myType);
        myMethod.Invoke(obj, null);
    

但是我发现程序集只能通过卸载appdomain来卸载。因此它需要在不同的可以卸载的应用程序域中运行。但我没有得到这个工作。我尝试了下面的代码,但它不起作用,因为 CreateInstanceFrom 返回类型 objecthandle 而不是我预期的 object。

        static private void ExecuteAssemblyMethodInAppDomain(string taskName, string className, string method)
    
        //create child domain and load assembly within it. the appdomain can than be unloaded.
        var dom = AppDomain.CreateDomain("taskDomain");
        var task = dom.CreateInstanceFrom($"tasks\\taskName.exe", $" taskName. className");
        Type myType = task.GetType();
        MethodInfo myMethod = myType.GetMethod(method);
        myMethod.Invoke(task, null);
        AppDomain.Unload(dom);
    

【问题讨论】:

您真的想将 EXE 加载到新的应用程序域中吗?这闻起来不对 - 为什么是 exe? 可执行吗?也许你可以开始一个新的进程。 【参考方案1】:

将cmets之后的答案更新为初始答案。

    使用 Unwrap 获取实例访问权限的直接方法不起作用,因为反射方法返回有关代理对象的信息,而不是目标类型的底层实例。

    要解决此限制,需要将“已知”类型的对象注入应用程序域,以便反射代码在应用程序域内工作。

代码:

HelperAssembly -- 包含一个简单的帮助类 Helper.cs

namespace HelperAssembly

  public class Helper : MarshalByRefObject
  
    public void CreateAndRunPlugin(string assemblyName, string typeName, string methodName)
    
      var task = AppDomain.CurrentDomain.CreateInstanceFromAndUnwrap(assemblyName, typeName);

      Type myType = task.GetType();
      MethodInfo myMethod = myType.GetMethod(methodName);
      myMethod.Invoke(task, null);

    
  

启动任务的例程:

  private static void TestAppDomain(string taskName, string className, string methodName)
  
    //create child domain and load assembly within it. the appdomain can than be unloaded.
    var dom = AppDomain.CreateDomain("taskDomain");
    try
    
      var whelper = dom.CreateInstanceFrom("path\\to\\HelperAssembly.dll", "HelperAssembly.Helper");
      var helper = (Helper)whelper.Unwrap();
       helper.CreateAndRunPlugin($"path\\to\\taskname.exe", $"taskName.className", methodName);
      return;

    
    finally
    
      AppDomain.Unload(dom);
    
  

【讨论】:

谢谢,但它还没有工作。解包返回错误 System.Runtime.Serialization.SerializationException: '类型 'c' 在程序集 'a' 中未标记为可序列化。'我尝试继承 MarshalByRefObject 但 getType() 返回 MarshlByRefObject 并且找不到该方法。 您的应用是否可以要求所有“taskName.ClassName”都从特定接口继承?有了这个限制,就可以轻松解决问题。否则,将需要一些技巧。

以上是关于在新的应用程序域中加载程序集的主要内容,如果未能解决你的问题,请参考以下文章

无法在新的 appDomain 中加载程序集

ApplicationManager.CreateObject 无法在新的 AppDomain 中加载程序集

在新的 AppDomain 中加载插件

在 AppDomain 问题中加载具有依赖项的程序集

在不同的 AppDomain 中加载具有依赖项的程序集

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