Addresses code quality review of a7f0732:
- LoadedPlugin now holds the AssemblyLoadContext explicitly so Task 10
can call Unload() for hot reload (Critical)
- LoadedPlugin.Error is Exception? to match PluginDiscoveryResult and
preserve stack traces; synthetic failures build FileNotFoundException
and InvalidOperationException (Important)
- PluginLoader falls back to ReflectionTypeLoadException.Types if
GetTypes() can't fully resolve (Important)
- Hardcoded abstractions assembly name is now a const (Minor)
60 lines
2.2 KiB
C#
60 lines
2.2 KiB
C#
using System.Reflection;
|
|
using AcDream.Plugin.Abstractions;
|
|
|
|
namespace AcDream.Core.Plugins;
|
|
|
|
public static class PluginLoader
|
|
{
|
|
/// <summary>
|
|
/// Load a plugin DLL from <paramref name="pluginDirectory"/> into a collectible
|
|
/// <see cref="System.Runtime.Loader.AssemblyLoadContext"/>, find the first type
|
|
/// implementing <see cref="IAcDreamPlugin"/>, instantiate it, and call its
|
|
/// <see cref="IAcDreamPlugin.Initialize"/> with the supplied host. Any failure
|
|
/// is returned as a failed <see cref="LoadedPlugin"/> rather than thrown.
|
|
/// </summary>
|
|
public static LoadedPlugin Load(string pluginDirectory, PluginManifest manifest, IPluginHost host)
|
|
{
|
|
var dllPath = Path.Combine(pluginDirectory, manifest.EntryDll);
|
|
if (!File.Exists(dllPath))
|
|
return new LoadedPlugin(
|
|
manifest,
|
|
Plugin: null,
|
|
LoadContext: null,
|
|
Error: new FileNotFoundException($"entry dll not found: {dllPath}", dllPath));
|
|
|
|
try
|
|
{
|
|
var alc = new PluginAssemblyLoadContext(pluginDirectory, dllPath);
|
|
var asm = alc.LoadFromAssemblyPath(dllPath);
|
|
|
|
IEnumerable<Type> types;
|
|
try
|
|
{
|
|
types = asm.GetTypes();
|
|
}
|
|
catch (ReflectionTypeLoadException rtle)
|
|
{
|
|
types = rtle.Types.OfType<Type>();
|
|
}
|
|
|
|
var pluginType = types
|
|
.FirstOrDefault(t => !t.IsAbstract && typeof(IAcDreamPlugin).IsAssignableFrom(t));
|
|
|
|
if (pluginType is null)
|
|
return new LoadedPlugin(
|
|
manifest,
|
|
Plugin: null,
|
|
LoadContext: null,
|
|
Error: new InvalidOperationException(
|
|
$"no IAcDreamPlugin implementation found in {manifest.EntryDll}"));
|
|
|
|
var instance = (IAcDreamPlugin)Activator.CreateInstance(pluginType)!;
|
|
instance.Initialize(host);
|
|
return new LoadedPlugin(manifest, instance, alc, Error: null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new LoadedPlugin(manifest, Plugin: null, LoadContext: null, Error: ex);
|
|
}
|
|
}
|
|
}
|