使用AppDomain将dll动态加载和卸载到我的项目中 [英] Load and unload a dll dynamically into my project using AppDomain

查看:91
本文介绍了使用AppDomain将dll动态加载和卸载到我的项目中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想动态地在当前项目的单独解决方案中使用来自不同项目的类.我认为解决方案是将dll加载到我的项目中.我使用下面的代码来完成我的任务,并且成功了.

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

但是,我现在的问题是我想卸载dll,但由于Assembly中没有卸载方法,所以无法执行此操作.我发现的解决方案是必须使用AppDomain加载程序集,然后再将其卸载.

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.

现在这是我的主要问题.我不断得到FileNotFoundException.这是我的代码:

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
}

最后一行是以下异常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.

我已经在上尝试了解决方案>链接,但对我没有任何帮助...我不断收到同样的异常.之后,我想卸载域,但是我无法解决第一个加载dll的问题.如何修复我的代码?

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

EDIT

当我将预期的dll复制到项目的同一bin文件夹中时,它可以工作.但是,我不想在项目中复制dll.有没有一种方法可以从其路径加载而不将其复制到我的bin文件夹中?

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?

推荐答案

您在代理域中定义一个GetAssembly方法,该方法会将加载的Assembly拉入主域.这使整个概念变得毫无意义,因为即使卸载代理域,您的主域最终也会被加载的程序集污染.

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.

与其在您的代理域中使用该程序集而不是返回该程序集.如果要将某些信息推回主域,则必须传递简单的可序列化类型(或从MarshalByRefObject派生的远程对象),以便主域保持干净.

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

您的代理应如下所示:

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.")
    }
}

用法:

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

最后是FileNotFoundException本身:

AppDomain中,您只能加载位于主域的同一文件夹或子文件夹中的程序集.使用它代替设置对象中的Environment.CurrentDirectory:

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

如果您真的想从任何位置加载程序集,请以byte[]的形式加载它:

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

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

这篇关于使用AppDomain将dll动态加载和卸载到我的项目中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆