#c_sharp #plugin #рефлексия
Захотелось научиться работать с отражением и вот этим всем. Начал с простенького, решил написать подключаемый плагин, который автоматически подгрузится из именной папки. Что бы не мучиться с отражением методов и прочей нечистью на ум конечно же приходит наследование и полиморфизм. То есть мне нужен какой-то базовый класс с начальным функционалом от которого будут наследоваться все другие плагины. То есть нужно сделать отдельную библиотеку с апи, которую будут использовать обе стороны (программа и плагин). public interface IPlugin { void Loaded(); } Вот такой класс придумал в этой библиотеке. Теперь я могу спокойно ее подключать к плагину и наследоваться. public class Test : IPlugin { #region Implementation of IPlugin public void Loaded() => Console.WriteLine( "Hello World" ); #endregion } Вот у меня уже две библиотеки, одна с моим апи, другая его использует. Теперь мне нужно как-то загрузить сборку с плагином и использовать его методы. Вот тут мне и нужна помощь. Так как библиотека плагина использует другую библиотеку с апи, я не смогу ее так просто загрузить, так как выскочит ошибка мол либе не хватает референсов. Как правильно загрузить плагин указав что сборка которая ему нужна уже загружена в программу? Вот код которым я пытаюсь что-то наколдовать: private static void Main ( string[] args ) { var pluginsPath = $"{Directory.GetCurrentDirectory()}\\Plugins"; var files = Directory.GetFiles( pluginsPath , "*.dll" ); foreach ( var file in files ) { Console.WriteLine( $"Trying load: {file}" ); Assembly assembly; try { assembly = Assembly.Load( file ); } catch ( Exception ex ) { Console.WriteLine( ex.Message ); continue; } var types = assembly.GetTypes().Where( type => type.IsClass && type.GetInterface( nameof( IPlugin ) ) != null ); foreach ( var type in types ) { var plugin = Activator.CreateInstance( type ) as IPlugin; if ( plugin == null ) { Console.WriteLine( "Null" ); continue; } plugin.Loaded(); } } } Падает еще на первом try с сообщением: Необработанное исключение типа "System.IO.FileLoadException" в mscorlib.dll Дополнительные сведения: Не удалось загрузить файл или сборку "C:\Users\anweledig\Documents\Visual Studio 2015\Projects\Anweledig\ConsoleApplication\bin\Debug\Plugins\TestPlugin.dll" либо одну из их зависимостей. Данное имя сборки или база кода недействительны. (Исключение из HRESULT: 0x80131047)
Ответы
Ответ 1
Вы можете просто загрузить другую библиотеку. Все ее зависимости будут автоматически загружены. Проблема может быть только в случае, если библиотека лежит не в той же папке, в которой лежит ваше приложение, и вы при этом загружаете ее через Assembly.LoadFrom. Чтобы это обойти, вам нужно указать рантайму что зависимости надо искать в папке плагина, а не в папке вашего приложения. Тогда можно будет использовать обычный Assembly.Load и заодно заработает станадартный механизм загрузки сборок. Для этого можно использовать создание аппдомена с указанием AppDomainSetup.ApplicationBase Т.е. схема примерно такая: У вас есть базовая dll для плагинов - MyApp.SDK, в ней базовый класс (а лучше - интерфейс): public abstract class Plugin { public abstract void Loaded(); public abstract void Unload(); } в ней же код загрузки и вызова плагина (для простоты) public static class PluginInvoker { public static void InvokePlugin() { var pluginAssembly = Assembly.Load("SomePlugin"); var pluginType = pluginAssembly.GetTypes().Where(t => typeof(Plugin).IsAssignableFrom(t)).Single(); Plugin plugin = (Plugin)pluginType.GetConstructor(new Type[0]).Invoke(null); plugin.Loaded(); } } есть реализация этого плагина в SomePlugin.dll: public class Test: MyApp.SDK.Plugin { public override void Loaded() { Console.WriteLine("Loaded"); } public override void Unload() { Console.WriteLine("Unloaded"); } } в SomePlugin есть референс на MyApp.SDK. И есть главное приложение, в котором есть ссылка на MyApp.SDK, но нет ссылки на SomePlugin: static void Main(string[] args) { AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = @"C:\Projects\MyApp\SomePlugin\bin\Debug\"; AppDomain pluginDomain = AppDomain.CreateDomain("plugin", null, setup); pluginDomain.DoCallBack(PluginInvoker.InvokePlugin); } ApplicationBase задает папку, откуда AppDomain будет загружать сборки - т.к. это папка плагина, то все, на что он ссылается, будет корректно загружено. Если плагины подгружаются из подпапки приложения (например, из Plugins), то в вместо AppDomainSetup.ApplicationBase можно использовать AppDomainSetup.PrivateBinPath. Если же при этом плагины вообще не планируется выгружать, и все плагины лежат в одной папке (без подпапок на каждый из плагинов) - то можно обойтись без отдельного AppDomain, просто дописав путь к папке с плагинами в секцию app.config/. код примера на github: https://github.com/PashaPash/PluginLoadSample
Комментариев нет:
Отправить комментарий