using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Metadata.Ecma335; using System.Text; using System.Threading.Tasks; namespace Pilz.Plugins { public class PluginManager where TPluginInterface : class where TPluginRuntimeInfo : PluginRuntimeInfo { 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) { 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 results = new List>(); foreach (var path in paths) results.Add(LoadPlugin(path, parameters)); return results; } /// /// Loads plugins from the given assemblies. /// /// /// /// public IEnumerable> LoadPlugins(Assembly[] assemblies, params object?[]? parameters) { var results = new List>(); foreach (var assembly in assemblies) results.Add(LoadPlugin(assembly, parameters)); return results; } /// /// 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) { var results = new List>(); if (listenAssemblyLoadContext) AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad; foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) results.Add(LoadPlugin(assembly, parameters)); return results; } /// /// Loads an assembly from the given file path and then loads the plugin from the assembly. /// /// /// /// public PluginLoadInfo LoadPlugin(string path, params object?[]? parameters) { var result = new PluginLoadInfo(); var loadContext = new PluginLoadContext(path); if (File.Exists(path)) { try { result.Assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path))); } catch { result.Status = PluginLoadStatus.ErrorAtLoading; } } else result.Status = PluginLoadStatus.FileNotFound; if (result.Assembly != null) LoadPlugin(result, parameters); return result; } /// /// Loads the plugin from a given assembly. /// /// /// /// public PluginLoadInfo LoadPlugin(Assembly assembly, params object?[]? parameters) { var result = new PluginLoadInfo { Assembly = assembly }; LoadPlugin(result); return result; } private void LoadPlugin(PluginLoadInfo result, params object?[]? parameters) { var irmplugin = typeof(TPluginInterface); if (result.Assembly == null) result.Status = PluginLoadStatus.NoValidPlugin; else if (loadedPlugins.Any(n => n.Assembly == result.Assembly)) result.Status = PluginLoadStatus.AlreadyLoaded; else { foreach (var type in result.Assembly.GetTypes()) { if (irmplugin.IsAssignableFrom(type)) { var info = Activator.CreateInstance(); info.Assembly = result.Assembly; result.PluginsInternal.Add(info); try { if (Activator.CreateInstance(type, parameters) is TPluginInterface plugin) { info.Plugin = plugin; info.Status = PluginStatus.Success; loadedPlugins.Add(info); } } catch { info.Status = PluginStatus.ErrorAtInitializing; } } } } if (result.PluginsInternal.Count == 0) result.Status = PluginLoadStatus.NoValidPlugin; } } }