add a simple plugin system

This commit is contained in:
2023-11-14 14:20:26 +01:00
parent 1c4d7554e2
commit 9b7eb40cfc
20 changed files with 563 additions and 6 deletions

7
Pilz.Plugins/IPlugin.cs Normal file
View File

@@ -0,0 +1,7 @@
namespace Pilz.Plugins
{
public interface IPlugin
{
public string Name { get; }
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Plugins
{
internal class PluginLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly? Load(AssemblyName assemblyName)
{
var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
return LoadFromAssemblyPath(assemblyPath);
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
var libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
return LoadUnmanagedDllFromPath(libraryPath);
return IntPtr.Zero;
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Plugins
{
public class PluginManager : PluginManager<IPlugin, PluginRuntimeInfo>
{
public static PluginManager Instance { get; private set; } = new();
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Plugins
{
public class PluginManager<TPluginInterface, TPluginRuntimeInfo> where TPluginInterface : class where TPluginRuntimeInfo : PluginRuntimeInfo<TPluginInterface>
{
public static IEnumerable<TPluginRuntimeInfo> LoadPlugins(string[] paths)
{
var states = new List<TPluginRuntimeInfo>();
foreach (string path in paths)
states.Add(LoadPlugin(path));
return states;
}
public static TPluginRuntimeInfo LoadPlugin(string path)
{
var info = Activator.CreateInstance<TPluginRuntimeInfo>();
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)));
}
catch
{
info.Status = PluginStatus.ErrorAtLoading;
}
}
else
info.Status = PluginStatus.FileNotFound;
if (assembly != null)
{
foreach (var type in assembly.GetTypes())
{
if (info.Plugin == null && irmplugin.IsAssignableFrom(type))
{
try
{
if (Activator.CreateInstance(type) is TPluginInterface plugin)
{
info.Plugin = plugin;
info.Status = PluginStatus.Success;
}
}
catch
{
info.Status = PluginStatus.ErrorAtInitializing;
}
}
}
if (info.Plugin == null)
info.Status = PluginStatus.NoValidPlugin;
}
return info;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Plugins
{
public class PluginRuntimeInfo : PluginRuntimeInfo<IPlugin>
{
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Plugins
{
public class PluginRuntimeInfo<T> where T : class
{
public T? Plugin { get; internal set; }
public PluginStatus Status { get; internal set; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Plugins
{
public enum PluginStatus
{
None,
Success,
FileNotFound,
ErrorAtLoading,
ErrorAtInitializing,
NoValidPlugin
}
}