diff --git a/Pilz.Plugins.Advanced/IPluginFeatureProvider.cs b/Pilz.Plugins.Advanced/IPluginFeatureProvider.cs new file mode 100644 index 0000000..23ee2a7 --- /dev/null +++ b/Pilz.Plugins.Advanced/IPluginFeatureProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Plugins.Advanced +{ + public interface IPluginFeatureProvider where T : PluginFeature + { + static abstract T Instance { get; } + } +} diff --git a/Pilz.Plugins.Advanced/IPluginFeaturesProvider.cs b/Pilz.Plugins.Advanced/IPluginFeaturesProvider.cs new file mode 100644 index 0000000..f5eca6e --- /dev/null +++ b/Pilz.Plugins.Advanced/IPluginFeaturesProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Plugins.Advanced +{ + public interface IPluginFeaturesProvider + { + static abstract PluginFeature[] GetFeatures(); + } +} diff --git a/Pilz.Plugins/PluginManagerT.cs b/Pilz.Plugins/PluginManagerT.cs index 44794a4..1603c2d 100644 --- a/Pilz.Plugins/PluginManagerT.cs +++ b/Pilz.Plugins/PluginManagerT.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Reflection.Metadata.Ecma335; using System.Text; using System.Threading.Tasks; @@ -9,38 +10,72 @@ namespace Pilz.Plugins { public class PluginManager where TPluginInterface : class where TPluginRuntimeInfo : PluginRuntimeInfo { - public IEnumerable LoadPlugins(string[] paths) + private readonly List loadedPlugins = []; + + /// + /// Returns a list of all currently loaded plugins. + /// + public IEnumerable LoadedPlugins => loadedPlugins.AsReadOnly(); + + private void CurrentDomain_AssemblyLoad(object? sender, AssemblyLoadEventArgs args) { - return LoadPlugins(paths, null); + LoadPlugin(args.LoadedAssembly); } + /// + /// Loads assembly from the given file paths and then loads the plugins from the assemblies. + /// + /// + /// + /// public IEnumerable LoadPlugins(string[] paths, params object?[]? parameters) { - var states = new List(); - - foreach (string path in paths) - states.Add(LoadPlugin(path)); - - return states; + foreach (var path in paths) + yield return LoadPlugin(path, parameters); } - public TPluginRuntimeInfo LoadPlugin(string path) + /// + /// Loads plugins from the given assemblies. + /// + /// + /// + /// + public IEnumerable LoadPlugins(Assembly[] assemblies, params object?[]? parameters) { - return LoadPlugin(path, null); + foreach (var assembly in assemblies) + yield return LoadPlugin(assembly, parameters); } + /// + /// Loads plugins from already loaded assemblies for the current . + /// + /// Do also load plugins from all yet not loaded assemblies by listening the event . + /// + public IEnumerable LoadOwnPlugins(bool listenAssemblyLoadContext, params object?[]? parameters) + { + if (listenAssemblyLoadContext) + AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad; + + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + yield return LoadPlugin(assembly, parameters); + } + + /// + /// Loads an assembly from the given file path and then loads the plugin from the assembly. + /// + /// + /// + /// public TPluginRuntimeInfo LoadPlugin(string path, params object?[]? parameters) { var info = Activator.CreateInstance(); - var irmplugin = typeof(TPluginInterface); var loadContext = new PluginLoadContext(path); - Assembly? assembly = null; if (File.Exists(path)) { try { - assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path))); + info.Assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path))); } catch { @@ -49,10 +84,38 @@ namespace Pilz.Plugins } else info.Status = PluginStatus.FileNotFound; + + if (info.Assembly != null) + info = LoadPlugin(info.Assembly, parameters); - if (assembly != null) + return info; + } + + /// + /// Loads the plugin from a given assembly. + /// + /// + /// + /// + public TPluginRuntimeInfo LoadPlugin(Assembly assembly, params object?[]? parameters) + { + var info = Activator.CreateInstance(); + info.Assembly = assembly; + LoadPlugin(info); + return info; + } + + private void LoadPlugin(TPluginRuntimeInfo info, params object?[]? parameters) + { + var irmplugin = typeof(TPluginInterface); + + if (info.Assembly == null) + info.Status = PluginStatus.NoValidPlugin; + else if (loadedPlugins.Any(n => n.Assembly == info.Assembly)) + info.Status = PluginStatus.AlreadyLoaded; + else { - foreach (var type in assembly.GetTypes()) + foreach (var type in info.Assembly.GetTypes()) { if (info.Plugin == null && irmplugin.IsAssignableFrom(type)) { @@ -62,6 +125,7 @@ namespace Pilz.Plugins { info.Plugin = plugin; info.Status = PluginStatus.Success; + loadedPlugins.Add(info); } } catch @@ -70,12 +134,10 @@ namespace Pilz.Plugins } } } - - if (info.Plugin == null) - info.Status = PluginStatus.NoValidPlugin; } - return info; + if (info.Plugin == null) + info.Status = PluginStatus.NoValidPlugin; } } } diff --git a/Pilz.Plugins/PluginRuntimeInfoT.cs b/Pilz.Plugins/PluginRuntimeInfoT.cs index a5f9f18..d6b427c 100644 --- a/Pilz.Plugins/PluginRuntimeInfoT.cs +++ b/Pilz.Plugins/PluginRuntimeInfoT.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -10,5 +11,6 @@ namespace Pilz.Plugins { public T? Plugin { get; internal set; } public PluginStatus Status { get; internal set; } + public Assembly? Assembly { get; internal set; } } } diff --git a/Pilz.Plugins/PluginStatus.cs b/Pilz.Plugins/PluginStatus.cs index 2c6b908..9dd4098 100644 --- a/Pilz.Plugins/PluginStatus.cs +++ b/Pilz.Plugins/PluginStatus.cs @@ -10,6 +10,7 @@ namespace Pilz.Plugins { None, Success, + AlreadyLoaded, FileNotFound, ErrorAtLoading, ErrorAtInitializing,