Skip to content

Commit

Permalink
feat: Refaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuisyuu committed Jul 22, 2023
1 parent 3273213 commit 022ea65
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 136 deletions.
41 changes: 0 additions & 41 deletions src/EntryPointAttribute.cs

This file was deleted.

6 changes: 2 additions & 4 deletions src/IModule.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
namespace Loader;

public interface IModule : IDisposable
public interface IPlugin
{
string Name { get; }
void Initialize();
void Unload();
void Initialize(PluginData pluginData);
}
6 changes: 6 additions & 0 deletions src/IPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Loader;

public interface IPlugin

Check failure on line 3 in src/IPlugin.cs

View workflow job for this annotation

GitHub Actions / build

The namespace 'Loader' already contains a definition for 'IPlugin'
{
public void Initialize(PluginData pluginData);

Check failure on line 5 in src/IPlugin.cs

View workflow job for this annotation

GitHub Actions / build

Type 'IPlugin' already defines a member called 'Initialize' with the same parameter types
}
10 changes: 6 additions & 4 deletions src/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ public static class Main
[UnmanagedCallersOnly]
public static void Initialize()
{
DirectoryInfo directoryInfo = new("plugins");
if (!directoryInfo.Exists)
if (!Directory.Exists("plugins"))
{
directoryInfo.Create();
Directory.CreateDirectory("plugins");
}
foreach (FileInfo file in PluginManager.EnumerableAllFiles())
{
PluginManager.Load(file);
}
PluginManager.ReloadAllPlugin();
}
}
13 changes: 13 additions & 0 deletions src/ModuleAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Loader;

public abstract class EntryPointAttributeBase : Attribute
{
internal abstract IPlugin CreateInstance();
}

[AttributeUsage(AttributeTargets.Assembly)]
public sealed class EntryPointAttribute<T> : EntryPointAttributeBase where T
: IPlugin, new()
{
internal override IPlugin CreateInstance() => new T();
}
48 changes: 48 additions & 0 deletions src/Plugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Reflection;
using System.Runtime.Loader;

namespace Loader;

public class PluginData
{
internal FileInfo FileInfo { get; }
internal AssemblyLoadContext AssemblyLoadContext { get; }
public event EventHandler? Unloading;

internal PluginData(FileInfo file, AssemblyLoadContext context)
{
FileInfo = file;
AssemblyLoadContext = context;
}

internal bool Load()
{
Assembly assembly;
try
{
assembly = AssemblyLoadContext.LoadFromAssemblyPath(FileInfo.FullName);
}
catch (BadImageFormatException)
{
return false;
}
EntryPointAttributeBase? entry = assembly.GetCustomAttribute<EntryPointAttributeBase>();
if (entry is null)
{
AssemblyLoadContext.Unload();
return false;
}
IPlugin plugin = entry.CreateInstance();
plugin.Initialize(this);
return true;
}

internal void Unload()
{
if (Unloading is not null)
{
Unloading(this, new());
}
AssemblyLoadContext.Unload();
}
}
24 changes: 24 additions & 0 deletions src/PluginData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Runtime.Loader;

namespace Loader;

public class PluginData

Check failure on line 5 in src/PluginData.cs

View workflow job for this annotation

GitHub Actions / build

The namespace 'Loader' already contains a definition for 'PluginData'
{
internal FileInfo FileInfo { get; }
internal AssemblyLoadContext AssemblyLoadContext { get; }
public event EventHandler? Unloading;

internal PluginData(FileInfo file, AssemblyLoadContext context)

Check failure on line 11 in src/PluginData.cs

View workflow job for this annotation

GitHub Actions / build

Type 'PluginData' already defines a member called 'PluginData' with the same parameter types
{
FileInfo = file;
AssemblyLoadContext = context;
}
internal void Unload()

Check failure on line 16 in src/PluginData.cs

View workflow job for this annotation

GitHub Actions / build

Type 'PluginData' already defines a member called 'Unload' with the same parameter types
{
if (Unloading is not null)
{
Unloading(this, new());
}
AssemblyLoadContext.Unload();
}
}
114 changes: 27 additions & 87 deletions src/PluginManager.cs
Original file line number Diff line number Diff line change
@@ -1,107 +1,47 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Runtime.Loader;

namespace Loader;

internal static class PluginManager
{
public static Dictionary<AssemblyLoadContext, List<IModule>> PluginContexts { get; }
private static readonly Dictionary<string, PluginData> _pluginContexts;
private static readonly string _pluginDirectoryPath;

static PluginManager()
{
PluginContexts = new();
_pluginContexts = new();
_pluginDirectoryPath = "plugins";
}

public static IEnumerable<string> EnumerableAllDll()
public static bool Load(FileInfo file)
{
DirectoryInfo directoryInfo = new("plugins");
if (!directoryInfo.Exists)
{
directoryInfo.Create();
}
foreach (var file in directoryInfo.EnumerateFiles("*.dll"))
{
yield return file.FullName;
}
AssemblyLoadContext loadContext = new(file.FullName);
PluginData pluginData = new(file, loadContext);
if (!pluginData.Load())
{
return false;
}
_pluginContexts[file.Name] = pluginData;
return true;
}

[MethodImpl(MethodImplOptions.Synchronized)]
public static void ReloadAllPlugin()
public static bool Unload(string name)
{
//uninstall loaded modules
foreach (var (ctx, unload) in PluginContexts)
{
try
{
foreach (var module in unload)
{
try
{
module.Unload();
}
catch (Exception e)
{
Console.Error.WriteLine(
"plugin {0} unload failed. file {1}. {2}",
module.Name,
ctx.Name,
e
);
}
}
ctx.Unload();
}
catch (Exception e)
{
Console.Error.WriteLine("assembly unload failed {0}. {1}", ctx.Name, e);
}
}
//clear PluginContexts
PluginContexts.Clear();
//dict for sort
SortedDictionary<
ModuleEntryPointAttributeBase,
(AssemblyLoadContext ctx, List<IModule> unload)
> allEntryPoint = new();
//enumerate all dll and load all EntryPointAttribute
//then sort by priority
foreach (var file in EnumerableAllDll())
{
try
{
AssemblyLoadContext loadContext = new(file);
Assembly assembly = loadContext.LoadFromAssemblyPath(file);
foreach (
var entryPoint in assembly.GetCustomAttributes<ModuleEntryPointAttributeBase>()
)
{
allEntryPoint.Add(entryPoint, new());
}
}
catch (Exception e)
{
Console.Error.WriteLine("{0} load failed. {1}", file, e);
}
}
//load all modules
foreach (var (entryPoint, (_, modules)) in allEntryPoint)
if (!_pluginContexts.ContainsKey(name))
{
try
{
var module = entryPoint.CreateInstance();
module.Initialize(); //initialize module
modules.Add(module);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
//add all modules to PluginContexts
foreach (var (_, (ctx, modules)) in allEntryPoint)
_pluginContexts[name].Unload();
_pluginContexts.Remove(name);
return true;
}

public static IEnumerable<FileInfo> EnumerableAllFiles()
{
DirectoryInfo directoryInfo = new(_pluginDirectoryPath);
foreach (FileInfo file in directoryInfo.EnumerateFiles())
{
PluginContexts.Add(ctx, modules);
yield return file;
}
}
}

0 comments on commit 022ea65

Please sign in to comment.