Files
Pilz/Pilz.Plugins/PluginManagerT.cs
2024-04-18 06:28:40 +02:00

152 lines
5.8 KiB
C#

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<TPluginInterface, TPluginRuntimeInfo> where TPluginInterface : class where TPluginRuntimeInfo : PluginRuntimeInfo<TPluginInterface>
{
private readonly List<TPluginRuntimeInfo> loadedPlugins = [];
/// <summary>
/// Returns a list of all currently loaded plugins.
/// </summary>
public IEnumerable<TPluginRuntimeInfo> LoadedPlugins => loadedPlugins.AsReadOnly();
private void CurrentDomain_AssemblyLoad(object? sender, AssemblyLoadEventArgs args)
{
LoadPlugin(args.LoadedAssembly);
}
/// <summary>
/// Loads assembly from the given file paths and then loads the plugins from the assemblies.
/// </summary>
/// <param name="paths"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public IEnumerable<PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo>> LoadPlugins(string[] paths, params object?[]? parameters)
{
foreach (var path in paths)
yield return LoadPlugin(path, parameters);
}
/// <summary>
/// Loads plugins from the given assemblies.
/// </summary>
/// <param name="paths"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public IEnumerable<PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo>> LoadPlugins(Assembly[] assemblies, params object?[]? parameters)
{
foreach (var assembly in assemblies)
yield return LoadPlugin(assembly, parameters);
}
/// <summary>
/// Loads plugins from already loaded assemblies for the current <see cref="AppDomain.CurrentDomain"/>.
/// </summary>
/// <param name="listenAssemblyLoadContext">Do also load plugins from all yet not loaded assemblies by listening the event <see cref="AppDomain.AssemblyLoad"/>.
/// <returns></returns>
public IEnumerable<PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo>> 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);
}
/// <summary>
/// Loads an assembly from the given file path and then loads the plugin from the assembly.
/// </summary>
/// <param name="path"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo> LoadPlugin(string path, params object?[]? parameters)
{
var result = new PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo>();
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;
}
/// <summary>
/// Loads the plugin from a given assembly.
/// </summary>
/// <param name="assembly"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo> LoadPlugin(Assembly assembly, params object?[]? parameters)
{
var result = new PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo>
{
Assembly = assembly
};
LoadPlugin(result);
return result;
}
private void LoadPlugin(PluginLoadInfo<TPluginInterface, TPluginRuntimeInfo> 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<TPluginRuntimeInfo>();
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;
}
}
}