From 022ea6568c386f784a8ca9d46cc8f16e505b0778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=A2=A6?= Date: Sat, 22 Jul 2023 20:52:16 +0800 Subject: [PATCH] feat: Refaction --- src/EntryPointAttribute.cs | 41 ------------- src/IModule.cs | 6 +- src/IPlugin.cs | 6 ++ src/Main.cs | 10 ++-- src/ModuleAttribute.cs | 13 +++++ src/Plugin.cs | 48 ++++++++++++++++ src/PluginData.cs | 24 ++++++++ src/PluginManager.cs | 114 +++++++++---------------------------- 8 files changed, 126 insertions(+), 136 deletions(-) delete mode 100644 src/EntryPointAttribute.cs create mode 100644 src/IPlugin.cs create mode 100644 src/ModuleAttribute.cs create mode 100644 src/Plugin.cs create mode 100644 src/PluginData.cs diff --git a/src/EntryPointAttribute.cs b/src/EntryPointAttribute.cs deleted file mode 100644 index 8ff5fb0..0000000 --- a/src/EntryPointAttribute.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Loader; - -public enum ModulePriority -{ - Library, - Front, - High, - Normal -} - -public abstract class ModuleEntryPointAttributeBase - : Attribute, - IComparer -{ - internal abstract IModule CreateInstance(); - internal ModulePriority Priority { get; set; } - - //for sort - public int Compare(ModuleEntryPointAttributeBase? x, ModuleEntryPointAttributeBase? y) - { - if (ReferenceEquals(x, y)) - return 0; - if (ReferenceEquals(null, y)) - return 1; - if (ReferenceEquals(null, x)) - return -1; - return x.Priority.CompareTo(y.Priority); - } -} - -[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] -public sealed class ModuleEntryPointAttribute : ModuleEntryPointAttributeBase - where T : IModule, new() -{ - public ModuleEntryPointAttribute(ModulePriority priority = ModulePriority.Normal) - { - Priority = priority; - } - - internal override IModule CreateInstance() => new T(); -} diff --git a/src/IModule.cs b/src/IModule.cs index fbfb71d..55cb5b8 100644 --- a/src/IModule.cs +++ b/src/IModule.cs @@ -1,8 +1,6 @@ namespace Loader; -public interface IModule : IDisposable +public interface IPlugin { - string Name { get; } - void Initialize(); - void Unload(); + void Initialize(PluginData pluginData); } diff --git a/src/IPlugin.cs b/src/IPlugin.cs new file mode 100644 index 0000000..a041ff6 --- /dev/null +++ b/src/IPlugin.cs @@ -0,0 +1,6 @@ +namespace Loader; + +public interface IPlugin +{ + public void Initialize(PluginData pluginData); +} diff --git a/src/Main.cs b/src/Main.cs index 85fe897..97d7476 100644 --- a/src/Main.cs +++ b/src/Main.cs @@ -7,11 +7,13 @@ public static class Main [UnmanagedCallersOnly] public static void Initialize() { - DirectoryInfo directoryInfo = new("plugins"); - if (!directoryInfo.Exists) + if (!Directory.Exists("plugins")) { - directoryInfo.Create(); + Directory.CreateDirectory("plugins"); + } + foreach (FileInfo file in PluginManager.EnumerableAllFiles()) + { + PluginManager.Load(file); } - PluginManager.ReloadAllPlugin(); } } diff --git a/src/ModuleAttribute.cs b/src/ModuleAttribute.cs new file mode 100644 index 0000000..4403ee7 --- /dev/null +++ b/src/ModuleAttribute.cs @@ -0,0 +1,13 @@ +namespace Loader; + +public abstract class EntryPointAttributeBase : Attribute +{ + internal abstract IPlugin CreateInstance(); +} + +[AttributeUsage(AttributeTargets.Assembly)] +public sealed class EntryPointAttribute : EntryPointAttributeBase where T + : IPlugin, new() +{ + internal override IPlugin CreateInstance() => new T(); +} diff --git a/src/Plugin.cs b/src/Plugin.cs new file mode 100644 index 0000000..7f1efbd --- /dev/null +++ b/src/Plugin.cs @@ -0,0 +1,48 @@ +using System.Reflection; +using System.Runtime.Loader; + +namespace Loader; + +public class PluginData +{ + internal FileInfo FileInfo { get; } + internal AssemblyLoadContext AssemblyLoadContext { get; } + public event EventHandler? Unloading; + + internal PluginData(FileInfo file, AssemblyLoadContext context) + { + FileInfo = file; + AssemblyLoadContext = context; + } + + internal bool Load() + { + Assembly assembly; + try + { + assembly = AssemblyLoadContext.LoadFromAssemblyPath(FileInfo.FullName); + } + catch (BadImageFormatException) + { + return false; + } + EntryPointAttributeBase? entry = assembly.GetCustomAttribute(); + if (entry is null) + { + AssemblyLoadContext.Unload(); + return false; + } + IPlugin plugin = entry.CreateInstance(); + plugin.Initialize(this); + return true; + } + + internal void Unload() + { + if (Unloading is not null) + { + Unloading(this, new()); + } + AssemblyLoadContext.Unload(); + } +} diff --git a/src/PluginData.cs b/src/PluginData.cs new file mode 100644 index 0000000..f49fd41 --- /dev/null +++ b/src/PluginData.cs @@ -0,0 +1,24 @@ +using System.Runtime.Loader; + +namespace Loader; + +public class PluginData +{ + internal FileInfo FileInfo { get; } + internal AssemblyLoadContext AssemblyLoadContext { get; } + public event EventHandler? Unloading; + + internal PluginData(FileInfo file, AssemblyLoadContext context) + { + FileInfo = file; + AssemblyLoadContext = context; + } + internal void Unload() + { + if (Unloading is not null) + { + Unloading(this, new()); + } + AssemblyLoadContext.Unload(); + } +} \ No newline at end of file diff --git a/src/PluginManager.cs b/src/PluginManager.cs index b76f1a9..acf42e5 100644 --- a/src/PluginManager.cs +++ b/src/PluginManager.cs @@ -1,107 +1,47 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.Loader; +using System.Runtime.Loader; namespace Loader; internal static class PluginManager { - public static Dictionary> PluginContexts { get; } + private static readonly Dictionary _pluginContexts; + private static readonly string _pluginDirectoryPath; static PluginManager() { - PluginContexts = new(); + _pluginContexts = new(); + _pluginDirectoryPath = "plugins"; } - public static IEnumerable EnumerableAllDll() + public static bool Load(FileInfo file) { - DirectoryInfo directoryInfo = new("plugins"); - if (!directoryInfo.Exists) - { - directoryInfo.Create(); - } - foreach (var file in directoryInfo.EnumerateFiles("*.dll")) - { - yield return file.FullName; - } + AssemblyLoadContext loadContext = new(file.FullName); + PluginData pluginData = new(file, loadContext); + if (!pluginData.Load()) + { + return false; + } + _pluginContexts[file.Name] = pluginData; + return true; } - [MethodImpl(MethodImplOptions.Synchronized)] - public static void ReloadAllPlugin() + public static bool Unload(string name) { - //uninstall loaded modules - foreach (var (ctx, unload) in PluginContexts) - { - try - { - foreach (var module in unload) - { - try - { - module.Unload(); - } - catch (Exception e) - { - Console.Error.WriteLine( - "plugin {0} unload failed. file {1}. {2}", - module.Name, - ctx.Name, - e - ); - } - } - ctx.Unload(); - } - catch (Exception e) - { - Console.Error.WriteLine("assembly unload failed {0}. {1}", ctx.Name, e); - } - } - //clear PluginContexts - PluginContexts.Clear(); - //dict for sort - SortedDictionary< - ModuleEntryPointAttributeBase, - (AssemblyLoadContext ctx, List unload) - > allEntryPoint = new(); - //enumerate all dll and load all EntryPointAttribute - //then sort by priority - foreach (var file in EnumerableAllDll()) - { - try - { - AssemblyLoadContext loadContext = new(file); - Assembly assembly = loadContext.LoadFromAssemblyPath(file); - foreach ( - var entryPoint in assembly.GetCustomAttributes() - ) - { - allEntryPoint.Add(entryPoint, new()); - } - } - catch (Exception e) - { - Console.Error.WriteLine("{0} load failed. {1}", file, e); - } - } - //load all modules - foreach (var (entryPoint, (_, modules)) in allEntryPoint) + if (!_pluginContexts.ContainsKey(name)) { - try - { - var module = entryPoint.CreateInstance(); - module.Initialize(); //initialize module - modules.Add(module); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } + return false; } - //add all modules to PluginContexts - foreach (var (_, (ctx, modules)) in allEntryPoint) + _pluginContexts[name].Unload(); + _pluginContexts.Remove(name); + return true; + } + + public static IEnumerable EnumerableAllFiles() + { + DirectoryInfo directoryInfo = new(_pluginDirectoryPath); + foreach (FileInfo file in directoryInfo.EnumerateFiles()) { - PluginContexts.Add(ctx, modules); + yield return file; } } }