Add collectible AssemblyLoadContext to support plugin unloading #5

This commit is contained in:
Stone_Red
2025-12-20 00:23:48 +01:00
parent 10733383d3
commit 780b506103
+25 -2
View File
@@ -13,6 +13,7 @@ using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Loader;
using System.Threading; using System.Threading;
using System.Timers; using System.Timers;
using System.Windows; using System.Windows;
@@ -32,6 +33,7 @@ public partial class PluginWindow : Window
private Thread? pluginThread; private Thread? pluginThread;
private System.Timers.Timer? updateTimer; private System.Timers.Timer? updateTimer;
private Plugin? pluginClassInstance; private Plugin? pluginClassInstance;
private readonly AssemblyLoadContext assemblyLoadContext;
public bool IsRunning { get; private set; } = true; public bool IsRunning { get; private set; } = true;
public PluginMetadata PluginMetadata { get; private set; } public PluginMetadata PluginMetadata { get; private set; }
@@ -78,6 +80,21 @@ public partial class PluginWindow : Window
Height = settings.Size.Y; Height = settings.Size.Y;
PluginFolderPath = pluginFolderPath; PluginFolderPath = pluginFolderPath;
assemblyLoadContext = new AssemblyLoadContext(pluginMetadata.Name, isCollectible: true);
assemblyLoadContext.Resolving += (context, assemblyName) =>
{
string assemblyPath = Path.Combine(PluginFolderPath, assemblyName.Name + ".dll");
if (File.Exists(assemblyPath))
{
return context.LoadFromAssemblyPath(assemblyPath);
}
else
{
_ = assemblyLoadContext.LoadFromAssemblyName(assemblyName);
}
return null;
};
} }
public PluginWindow(Plugin pluginClassInstance, PluginMetadata pluginMetadata, PluginSettings settings) : this(pluginMetadata, settings, string.Empty) public PluginWindow(Plugin pluginClassInstance, PluginMetadata pluginMetadata, PluginSettings settings) : this(pluginMetadata, settings, string.Empty)
@@ -199,7 +216,11 @@ public partial class PluginWindow : Window
object? instance = pluginClassInstance; object? instance = pluginClassInstance;
if (instance is null) if (instance is null)
{ {
Assembly dll = Assembly.LoadFrom($"{PluginFolderPath}\\main.dll"); byte[] assemblyData = File.ReadAllBytes($"{PluginFolderPath}\\main.dll");
using MemoryStream assemblyStream = new(assemblyData);
Assembly dll = assemblyLoadContext.LoadFromStream(assemblyStream);
Type? instanceType = Array.Find(dll.GetTypes(), type => type.GetTypeInfo().BaseType == typeof(Plugin)); Type? instanceType = Array.Find(dll.GetTypes(), type => type.GetTypeInfo().BaseType == typeof(Plugin));
if (instanceType is null) if (instanceType is null)
@@ -216,7 +237,7 @@ public partial class PluginWindow : Window
if (instance is Plugin plugin) if (instance is Plugin plugin)
{ {
pluginClassInstance = plugin; pluginClassInstance = plugin;
pluginClassInstance.Application = new Plugins.PluginData(this, settings); pluginClassInstance.Application = new PluginData(this, settings);
} }
else else
{ {
@@ -437,6 +458,8 @@ public partial class PluginWindow : Window
{ {
App.Logger.LogError($"\"{PluginMetadata.Name}\" - {ex}", source: "Plugin"); App.Logger.LogError($"\"{PluginMetadata.Name}\" - {ex}", source: "Plugin");
} }
assemblyLoadContext.Unload();
} }
#region Window Events #region Window Events