using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace PKHeX.WinForms { public static class PluginLoader { public static IEnumerable LoadPlugins(string pluginPath) where T : class { var dllFileNames = !Directory.Exists(pluginPath) ? Enumerable.Empty() : Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.AllDirectories); var assemblies = GetAssemblies(dllFileNames); var pluginTypes = GetPluginsOfType(assemblies); return LoadPlugins(pluginTypes); } private static IEnumerable LoadPlugins(IEnumerable pluginTypes) where T : class { foreach (var t in pluginTypes) { var activate = (T?) Activator.CreateInstance(t); if (activate != null) yield return activate; } } private static IEnumerable GetAssemblies(IEnumerable dllFileNames) { #if UNSAFEDLL var assemblies = dllFileNames.Select(Assembly.UnsafeLoadFrom); #else var assemblies = dllFileNames.Select(Assembly.LoadFrom); #endif #if MERGED assemblies = assemblies.Concat(new[] { Assembly.GetExecutingAssembly() }); // load merged too #endif return assemblies; } private static IEnumerable GetPluginsOfType(IEnumerable assemblies) { var pluginType = typeof(T); return assemblies.Where(z => z != null).SelectMany(z => GetPluginTypes(z, pluginType)); } private static IEnumerable GetPluginTypes(Assembly z, Type pluginType) { try { var types = z.GetTypes(); return types.Where(type => IsTypePlugin(type, pluginType)); } #pragma warning disable CA1031 // Do not catch general exception types // User plugins can be out of date, with mismatching API surfaces. catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { System.Diagnostics.Debug.WriteLine($"Unable to load plugin [{pluginType.Name}]: {z.FullName}", ex.Message); return Enumerable.Empty(); } } private static bool IsTypePlugin(Type type, Type pluginType) { if (type.IsInterface || type.IsAbstract) return false; var name = pluginType.FullName; if (name == null) return false; if (type.GetInterface(name) == null) return false; return true; } } }