-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathPluginUtilities.cs
More file actions
148 lines (116 loc) · 5.07 KB
/
PluginUtilities.cs
File metadata and controls
148 lines (116 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
namespace RegExpressLibrary;
/*
* See: https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support
*/
public class PluginLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public PluginLoadContext( string pluginPath )
{
_resolver = new AssemblyDependencyResolver( pluginPath );
}
protected override Assembly? Load( AssemblyName assemblyName )
{
string? assemblyPath = _resolver.ResolveAssemblyToPath( assemblyName );
return assemblyPath != null ? LoadFromAssemblyPath( assemblyPath ) : null;
}
protected override IntPtr LoadUnmanagedDll( string unmanagedDllName )
{
string? libraryPath = _resolver.ResolveUnmanagedDllToPath( unmanagedDllName );
return libraryPath != null ? LoadUnmanagedDllFromPath( libraryPath ) : IntPtr.Zero;
}
}
public static class PluginLoader
{
public static readonly JsonSerializerOptions JsonOptions = new( ) { AllowTrailingCommas = true, IncludeFields = true, ReadCommentHandling = JsonCommentHandling.Skip, WriteIndented = true };
public static async Task<(IReadOnlyList<RegexPlugin>? plugins, IReadOnlyList<RegexPlugin>? noFmPlugins)> LoadEngines( Window ownerWindow, string enginesJsonPath )
{
// -- deserialize "Engines.json"
EnginesData? engines_data;
try
{
using FileStream plugins_stream = File.OpenRead( enginesJsonPath );
engines_data = await JsonSerializer.DeserializeAsync<EnginesData>( plugins_stream, JsonOptions );
Debug.WriteLine( $"Total {engines_data?.engines?.Length} paths" );
}
catch( Exception exc )
{
if (InternalConfig.HandleException($"Failed to load plugins using '{enginesJsonPath}'", exc ))
throw;
return (null, null);
}
// --- load plugins and their engines
string plugin_root_folder = Path.GetDirectoryName( enginesJsonPath )!;
List<RegexPlugin> plugins = [];
List<RegexPlugin> no_fm_plugins = [];
if( engines_data?.engines != null )
{
foreach( EngineData engine_data in engines_data.engines )
{
if ( InternalConfig.limited_engine_dlls?.Length > 0 && !InternalConfig.limited_engine_dlls.Any( dll => engine_data.path.Contains(dll, StringComparison.CurrentCultureIgnoreCase) ) )
{
Debug.WriteLine( $"Skipping plugin due to limited_engine_dlls \"{engine_data.path}\"..." );
continue;
}
string plugin_absolute_path = Path.Combine( plugin_root_folder, engine_data.path! );
try
{
Debug.WriteLine( $"Trying to load plugin \"{plugin_absolute_path}\"..." );
PluginLoadContext load_context = new( plugin_absolute_path );
var assembly = load_context.LoadFromAssemblyName( new AssemblyName( Path.GetFileNameWithoutExtension( plugin_absolute_path ) ) );
var plugin_type = typeof( RegexPlugin );
foreach( Type type in assembly.GetTypes( ) )
{
if( plugin_type.IsAssignableFrom( type ) )
{
try
{
Debug.WriteLine( $"Making plugin \"{type.FullName}\"..." );
RegexPlugin plugin = (RegexPlugin)Activator.CreateInstance( type )!;
plugins.Add( plugin );
if( engine_data.no_fm ) no_fm_plugins.Add( plugin );
}
catch( Exception exc )
{
if (InternalConfig.HandleException( $"Failed to create plugin \"{engine_data.path}\"", exc ))
throw;
}
}
}
}
catch( Exception exc )
{
if (InternalConfig.HandleException( $"Failed to create plugin \"{engine_data.path}\"", exc ))
throw;
}
}
}
#if DEBUG
Debug.WriteLine( $"Total plugins: {plugins.Count}" );
foreach( var p in plugins )
{
foreach( var eng in p.GetEngines( ) )
{
Debug.WriteLine( $" {eng.Kind} {eng.Version}" );
}
}
#endif
if( plugins.Count == 0 )
{
MessageBox.Show( ownerWindow, $"No engines loaded using '{plugin_root_folder}'.\r\n", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation );
return (null, null);
}
return (plugins, no_fm_plugins);
}
}