0
votes

I'm working on a service which needs different assemblies, but at compile time we don't know which ones. Therefore I want to dynamically load an unload different assemblies. I had the loading working with this code,

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);
    }

But I found out the assembly can only be unloaded by unloading the appdomain. Therefore it needs to run in a different appdomain which can be unloaded. But I do not get this working. I tried the code below, but it does not work because CreateInstanceFrom return type objecthandle and not object as I expected.

        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);
    }
1
You really want to load an EXE into a new appdomain? This smells wrong - why an exe?TomTom
It's executable? Maybe you can start a new process.vernou

1 Answers

2
votes

Updated the answer after the comments to the initial answer.

  1. The straightforward approach that uses Unwrap to gain access to the instance doesn't work because reflection methods return information about the proxy object, not by the underlying instance of the target type.

  2. To work around this limitation, one needs to inject an object of a 'known' type into the application domain, so that the reflection code works inside the app domain.

Code:

HelperAssembly -- contains a simple helper class 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);

    }
  }
}

The routine that launches the task:

  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);
    }
  }