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

查看:20
本文介绍了使用 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
}

最后一行出现以下异常 无法加载文件或程序集dll,版本=1.0.0.0,Culture=neutral,PublicKeyToken=null"或其依赖项之一.系统找不到指定的文件.

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.

我已经尝试了这个的解决方案链接,但没有什么对我有用......我一直遇到同样的例外.之后我想卸载域,但是我无法解决加载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?

编辑

当我将预期的 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天全站免登陆