using System; using System.Collections.Generic; 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) { foreach (var path in paths) yield return LoadPlugin(path, parameters); } /// /// Loads plugins from the given assemblies. /// /// /// /// public IEnumerable LoadPlugins(Assembly[] assemblies, params object?[]? parameters) { 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 loadContext = new PluginLoadContext(path); if (File.Exists(path)) { try { info.Assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path))); } catch { info.Status = PluginStatus.ErrorAtLoading; } } else info.Status = PluginStatus.FileNotFound; if (info.Assembly != null) info = LoadPlugin(info.Assembly, parameters); 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 info.Assembly.GetTypes()) { if (info.Plugin == null && irmplugin.IsAssignableFrom(type)) { 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 (info.Plugin == null) info.Status = PluginStatus.NoValidPlugin; } } }