Load and unload a dll dynamically into my project using AppDomain

。_饼干妹妹 提交于 2019-12-06 16:15:18

问题


I want to use a class from a different project in a separate solution in my current project dynamically. I thought the solution is to load the dll into my project. I used the following code to do my task and it worked.

string dllPath = @"the path of my dll";
var DLL = Assembly.LoadFile(dllPath);
foreach (Type type in DLL.GetExportedTypes())
{
      if (type.Name == "targetClassName")
      {
          var c = Activator.CreateInstance(type);
          try
          {
              type.InvokeMember("myMethod", BindingFlags.InvokeMethod, null, c, new object[] { "Params" });
          }
          catch(Exception ex)
          {
             MessageBox.Show(ex.Message);
          }
          break;
      }
}

However, my problem now is that I want to unload the dll, which I can't do because there is no unload method in Assembly. The solution I found is that I must use AppDomain to load the assembly and then unload it.

Now here is my main problem. I keep getting FileNotFoundException. Here is my code:

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

private void BuildButton_Click(object sender, EventArgs e)
{
    string dllPath = @"DllPath";
    string dir = @"directory Path of the dll";
    AppDomainSetup domaininfo = new AppDomainSetup();
    domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
    Evidence adevidence = AppDomain.CurrentDomain.Evidence;
    AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

    Type Domtype = typeof(ProxyDomain);
    var value = (ProxyDomain)domain.CreateInstanceAndUnwrap(
         Domtype.Assembly.FullName,
         Domtype.FullName);

    var DLL = value.GetAssembly(dllPath);

    // Then use the DLL object as before
}

The last line is making the following exception Could not load file or assembly 'dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

I have tried the solutions on this link but nothing is working for me...I keep getting the same exception. After that I want to unload the domain, but I can't get through with the first problem of loading the dll. How to fix my code?

EDIT

When I copy the intended dll in the same bin folder of my project, it works. However, I don't want to copy the dll in my project. Is there a way to load it from its path without copying it to my bin folder?


回答1:


You define a GetAssembly method in your proxy domain, which pulls the loaded Assembly into the main domain. This renders the whole concept pointless, because even if you unload the proxy domain, your main domain will be eventually polluted by the loaded assembly.

Instead of returning the assembly just use it inside your proxy domain. If you want to push back some information into the main domain you must pass simple serializable types (or remote objects derived from MarshalByRefObject) so the main domain remains clean.

This is how you should do it:

// This class provides callbacks to the host app domain.
// This is optional, you need only if you want to send back some information
public class DomainHost : MarshalByRefObject
{
    // sends any object to the host. The object must be serializable
    public void SendDataToMainDomain(object data)
    {
        Console.WriteLine($"Hmm, some interesting data arrived: {data}");
    }

    // there is no timeout for host
    public override object InitializeLifetimeService() => null;
}

And your proxy should look like this:

class AssemblyLoader : MarshalByRefObject
{
    private DomainHost host;

    public void Initialize(DomainHost host)
    {
        // store the remote host here so you will able to use it to send feedbacks
        this.host = host;
        host.SendData("I am just being initialized.")
    }

    // of course, if your job has some final result you can have a return value
    // and then you don't even may need the DomainHost.
    // But do not return any Type from the loaded dll (not mentioning the whole Assembly).
    public void DoWork()
    {
        host.SendData("Work started. Now I will load some dll.");
        // TODO: load and use dll
        host.SendData(42);

        host.SendData("Job finished.")
    }
}

Usage:

var domain = AppDomain.CreateDomain("SandboxDomain");
var loader = (AssemblyLoader)domain.CreateInstanceAndUnwrap(typeof(AssemblyLoader).Assembly.FullName, typeod(AssemblyLoader).FullName);

// pass the host to the domain (again, this is optional; just for feedbacks)
loader.Initialize(new DomainHost());

// Start the work.
loader.DoWork();

// At the end, you can unload the domain
AppDomain.Unload(domain);

And finally for the FileNotFoundException itself:

In an AppDomain you can only load assemblies, which reside in the same or a subfolder of the main domain. Use this instead of Environment.CurrentDirectory in the setup object:

var setup = new AppDomainSetup
{
    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
    PrivateBinPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};

If you really want to load an assembly from any location, load it as a byte[]:

var dll = Assembly.Load(File.ReadAllBytes(fullPathToDll));


来源:https://stackoverflow.com/questions/50588618/load-and-unload-a-dll-dynamically-into-my-project-using-appdomain

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!