将 DLL 动态加载到单独的应用程序域中,然后卸载

Posted

技术标签:

【中文标题】将 DLL 动态加载到单独的应用程序域中,然后卸载【英文标题】:Loading DLL dynamically into separated app domain then unload 【发布时间】:2019-09-20 10:17:44 【问题描述】:

我正在尝试将 DLL 文件加载到单独的应用程序域中并调用 DLL 文件中的方法并从中获得一些响应。应用程序启动时项目bin文件夹中不存在DLL文件,DLL文件是从另一个文件夹加载的。完成 DLL 文件后,我想卸载刚刚创建的应用程序域。

步骤:

    创建了一个新的应用程序域 将我想要的 DLL 加载到应用程序域 调用方法并获得响应 卸载应用域

这是我目前尝试过的

这是 MyAssembly.dll 中的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace MyAssembly

    public class MyClass
    
        public static string MyMethod()
        
            return "Hello there, this is message from MyAssembly";
        
    

这是我加载 DLL 文件的方式

using System.Diagnostic;
using System.IO;

private class ProxyClass : MarshalByRefObject

    public void LoadAssembly()
    
        AppDomain dom;
        string domainName = "new:" + Guid.NewGuid();
        try
        
            //Create the app domain
            dom = AppDomain.CreateDomain(domainName, null, new AppDomainSetup
                    
                        PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"),
                        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
                        ShadowCopyFiles = "true",
                        ShadowCopyDirectories = "true",
                        LoaderOptimization = LoaderOptimization.SingleDomain,
                    );

            string dllPath = @"C:\MyProject\MyAssembly.dll";//the path to my assembly file I want to load
            //load the assembly to the new app domain
            Assembly asm = dom.Load(File.ReadAllBytes(dllPath));//Error occurred at here

            Type baseClass = asm.GetType("MyAssembly.MyClass");
            MethodInfo targetMethod = baseClass.GetMethod("MyMethod");

            string result = targetMethod.Invoke(null, new object[]);

            /*Do something to the result*/
        
        catch(Exception ex)
        
            Debug.WriteLine(ex.Message);
            Debug.WriteLine(ex.ToString());
        
        finally
        
            //Finally unload the app domain
            if (dom != null) AppDomain.Unload(dom);
        
    


public void BeginLoadDll()
    
        ProxyClass proxy = new ProxyClass();
        proxy.LoadAssembly();

        //OR like this, which gave me same error message as well
        //var dom = AppDomain.CreateDomain("new:" + Guid.NewGuid(), null, new AppDomainSetup
        //    
        //        PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"),
        //        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
        //        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
        //        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
        //        ShadowCopyFiles = "true",
        //        ShadowCopyDirectories = "true",
        //        LoaderOptimization = LoaderOptimization.SingleDomain,
        //    );
        //ProxyClass proxy = (ProxyClass)dom.CreateInstanceAndUnwrap(
        //    typeof(ProxyClass).Assembly.FullName, typeof(ProxyClass).FullName);
        //pr.LoadAssembly(watcherData, filePath);
    

这是我迄今为止观察到的一些东西,我不确定这只是我还是我错过了什么

-如果应用程序启动前项目bin文件夹中存在“MyAssembly.dll”,我可以加载dll文件

-如果“MyAssembly.dll”在应用程序启动之前项目 bin 文件夹中不存在,而是加载到项目 bin 文件夹以外的其他位置,我无法加载 dll 文件。比如项目bin文件夹是“C:\Main\MyMainProject\MyMainProject\bin”,DLL是从C:\MyProject\MyAssembly.dll加载的

-如果我将“MyAssembly.dll”文件移动到 bin 文件夹中(使用 File.Copy()File.Move()),它会以某种方式停止其余代码的执行。

我收到的错误信息

Could not load file or assembly 'MyAssembly, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=2c20c56a5e1f4bd4' or one of its dependencies.
The system cannot find the file specified.

编辑

我知道我可以使用Assembly.LoadFrom(@"PATH\TO\MY\DLL"),但这个问题是我无法卸载 DLL

【问题讨论】:

您需要先为您尝试加载的dll加载依赖项。 sn -p中的代码与你描述的行为不一致。 2)对于常规程序集加载是正常的,如果它失败一次,那么它将始终失败,即使在复制文件后可能也是如此。但不是使用 Load(byte[]),它根本没有看到负载,所以不能再次失败。 3) 完全不正常,不能通过拷贝文件来重启程序。所以还有一些实际加载程序集的代码,如果你搜索 FileSystemWatcher,你可能会找到它。 【参考方案1】:

经过几天的研究,我终于让它工作了。下面是我的最终工作代码。

帮助我实现这一目标的有用参考链接

https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.createinstanceandunwrap?view=netframework-4.8#System_AppDomain_CreateInstanceAndUnwrap_System_String_System_String_

C# reflection - load assembly and invoke a method if it exists

Using AppDomain in C# to dynamically load and unload dll

MyAssembly.dll 中的代码与问题中的代码相同。我也意识到我也可以返回一个对象类型。

如何将 DLL 文件加载到单独的应用程序域并卸载应用程序域

public void MethodThatLoadDll()

    AppDomain dom = null;
    //declare this outside the try-catch block, so we can unload it in finally block

    try
    
        string domName = "new:" + Guid.NewGuid();
        //assume that the domName is "new:50536e71-51ad-4bad-9bf8-67c54382bb46"

        //create the new domain here instead of in the proxy class
        dom = AppDomain.CreateDomain(, null, new AppDomainSetup
                    
                        PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"),
                        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
                        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
                        ShadowCopyFiles = "true",
                        ShadowCopyDirectories = "true",/*yes they are string value*/
                        LoaderOptimization = LoaderOptimization.SingleDomain,
                        DisallowBindingRedirects = false,
                        DisallowCodeDownload = true,
                    );
        ProxyClass proxy = (ProxyClass)dom.CreateInstanceAndUnwrap(
                    typeof(ProxyClass).Assembly.FullName, typeof(ProxyClass).FullName);
        string result = proxy.ExecuteAssembly("MyParam");
        /*Do whatever to the result*/
    
    catch(Exception ex)
    
        //handle the error here
    
    finally
    
        //finally unload the app domain
        if(dom != null) AppDomain.Unload(dom);
    


我的类继承MarshalByRefObject

private class ProxyClass : MarshalByRefObject

    //you may specified any parameter you want, if you get `xxx is not marked as serializable` error, see explanation below
    public string ExecuteAssembly(string param1)
    
        /*
         * All the code executed here is under the new app domain that we just created above
         * We also have different session state here, so if you want data from main domain's session, you should pass it as a parameter
         */
        //load your DLL file here
        Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName);
        //will print "new:50536e71-51ad-4bad-9bf8-67c54382bb46" which is the name that we just gave to the new created app domain

        Assembly asm = Assembly.LoadFrom(@"PATH/TO/THE/DLL");

        Type baseClass = asm.GetType("MyAssembly.MyClass");
        MethodInfo targetMethod = baseClass.GetMethod("MyMethod");

        string result = targetMethod.Invoke(null, new object[]);

        return result;
    

您可能会遇到的常见错误

'xxx' is not marked as serializable

如果您尝试像这样将自定义类作为参数传递,可能会发生这种情况

public void ExecuteAssembly(MyClass param1)

在这种情况下,将[Serializable] 放入MyClass,像这样

[Serializable]
public class MyClass  

【讨论】:

如果没有其他答案,我会在 2 天内将此标记为答案

以上是关于将 DLL 动态加载到单独的应用程序域中,然后卸载的主要内容,如果未能解决你的问题,请参考以下文章

我想在单独的应用程序域中动态卸载程序集,但它不起作用

将 c# 程序集动态加载和卸载到 appdomain

C#.Net 如何动态加载与卸载程序集(.dll或者.exe)3---- 动态加载Assembly应用程序

C#动态加载dll 时程序集的卸载问题

C#中动态加载和卸载DLL

C#.Net 如何动态加载与卸载程序集(.dll或者.exe)