【问题标题】:C# Unload managed assembly dll libraryC# 卸载托管程序集 dll 库
【发布时间】:2023-02-06 03:39:40
【问题描述】:

基于this Microsoft example,我如何通过在AssemblyLoadContext类中实现它来Unload加载程序集? (考虑到他们在全球List<Assembly>

我尝试了一些方法,this example too 但似乎没有什么能真正释放 dll 并让我在不关闭主应用程序的情况下用新的“删除它”或“覆盖它”。

实际加载代码是这样的:

static IEnumerable<ICommand> Plugs = Enumerable.Empty<ICommand>();
static readonly List<Assembly> PluginAssemblies = new();
static readonly List<string> PluginPath = new();

PluginPath.Add("C:\\...\\Plugin1.dll");
PluginPath.Add("C:\\...\\Plugin2.dll");
PluginPath.Add("C:\\...\\Plugin3.dll");
PluginPath.Add("C:\\...\\Plugin4.dll");

PluginPath.ForEach(P => { PluginAssemblies.Add(LoadPlugin(P)); });

Plugs = Plugs.Concat(PluginAssemblies.SelectMany(A => CreateCommands(A)));

这些是示例中的函数:

static Assembly LoadPlugin(string relativePath)
{
    // Navigate up to the solution root
    string root = Path.GetFullPath(Path.Combine(
        Path.GetDirectoryName(
            Path.GetDirectoryName(
                Path.GetDirectoryName(
                    Path.GetDirectoryName(
                        Path.GetDirectoryName(typeof(Program).Assembly.Location)))))));

    string pluginLocation = Path.GetFullPath(Path.Combine(root, relativePath.Replace('\\', Path.DirectorySeparatorChar)));
    Console.WriteLine($"Loading commands from: {pluginLocation}");
    PluginLoadContext loadContext = new PluginLoadContext(pluginLocation);
    return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(pluginLocation)));
}
static IEnumerable<ICommand> CreateCommands(Assembly assembly)
{
    int count = 0;

    foreach (Type type in assembly.GetTypes())
    {
        if (typeof(ICommand).IsAssignableFrom(type))
        {
            ICommand result = Activator.CreateInstance(type) as ICommand;
            if (result != null)
            {
                count++;
                yield return result;
            }
        }
    }

    if (count == 0)
    {
        string availableTypes = string.Join(",", assembly.GetTypes().Select(t => t.FullName));
        throw new ApplicationException(
            $"Can't find any type which implements ICommand in {assembly} from {assembly.Location}.\n" +
            $"Available types: {availableTypes}");
    }
}
using System;
using System.Reflection;
using System.Runtime.Loader;

namespace AppWithPlugin
{
    class PluginLoadContext : AssemblyLoadContext
    {
        private AssemblyDependencyResolver _resolver;

        public PluginLoadContext(string pluginPath)
        {
            _resolver = new AssemblyDependencyResolver(pluginPath);
        }

        protected override Assembly Load(AssemblyName assemblyName)
        {
            string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
            if (assemblyPath != null)
            {
                return LoadFromAssemblyPath(assemblyPath);
            }

            return null;
        }

        protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
        {
            string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
            if (libraryPath != null)
            {
                return LoadUnmanagedDllFromPath(libraryPath);
            }

            return IntPtr.Zero;
        }
    }
}

【问题讨论】:

  • 您的目标是什么版本的 .NET? AppDomains 和程序集加载的行为随着 .NET Core 发生了很大变化,并被带到 .NET 5 及更高版本中。

标签: c# .net .net-core dll .net-assembly


【解决方案1】:

根据微软的说法,在这里,How to use and debug assembly unloadability in .NET

调用 AssemblyLoadContext.Unload 方法只是启动 卸载

您需要满足一些条件才能完成。

  1. 没有线程将来自程序集的方法加载到其调用堆栈上的 AssemblyLoadContext 中。

  2. 加载到 AssemblyLoadContext 中的程序集中的任何类型、这些类型的实例以及程序集本身都不会被以下对象引用:

    • AssemblyLoadContext 之外的引用,弱引用(WeakReference 或 WeakReference)除外。

    • 强大的垃圾收集器 (GC) 从 AssemblyLoadContext 的内部和外部处理(GCHandleType.Normal 或 GCHandleType.Pinned)。

    简而言之,您需要确保内存中没有引用到您加载的程序集中的任何内容并等待 GC 完成其工作。

    看来您正在尝试加载程序集,保留代码并从内存中卸载程序集以保持分配的“命令”。那是做不到的。

    此外,您似乎没有从PluginAssemblies 列表中删除对程序集的引用。您也需要从插头数组中删除“命令”(或使用弱引用)。此外,我将从 LoadPlugin 方法返回一个元组 (Assembly, AssemblyLoadContext),以便在您使用完该命令后能够调用 Unload。

【讨论】:

    猜你喜欢
    • 2015-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 2011-04-29
    • 1970-01-01
    • 2010-09-17
    相关资源
    最近更新 更多