Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Пример системы плагинов

Автор: OrdJONY
Источник: www.progblog.ru
Дата: 3 августа 2010 года

Придумал я себе такую задачу, как разработать к программе систему плагинов (add-in-ов). Немного поковыряв AddIn которые были предложены начиная с .NET 3.5, понял что они мне не подходят. Во-первых это не удобное расположение каталогов, много лишних. Во-вторых не возможно внедриться в какую нибудь часть процесса и сделать по своему, т.е. слабая расширяемость. И решил изобрести велосипед…

В основу я поставил изолированность от главного приложения и возможность песочницы. Это все не сложно сделать создав новый AppDomain и сконфигурировав его на InternetZone. Но при разработке я столкнулся с проблемой при загрузке сборки с плагином в домен. Проблема заключалась в ошибке со связями. Для загрузки я использую AppDomain.Load(). Как позже оказалось этот метод мне не подходит. По-этому пришлось сделать не большой костыль, о котором я и расскажу.

Начнем с создания нового проекта и консольного приложения (я назвал его PluginHost) и добавим еще один проект библиотеку классов (назовем её PluginFramework). В библиотеку добавим класс:

public abstract class Plugin: MarshalByRefObject
{
   public virtual void Initialize()
   {
   }
}

И еще один класс, без которого было сложно реализовать необходимую мне логику:

public class AssemblyHelper: MarshalByRefObject
{
private AppDomain _currentDomain;
public AssemblyHelper()
{
_currentDomain = AppDomain.CurrentDomain;
_currentDomain.AssemblyResolve += new ResolveEventHandler(_currentDomain_AssemblyResolve);
}
Assembly _currentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
{
string[] nameSplit = e.Name.Split(',');
string path = Path.Combine(SearchFolder, nameSplit[0] + ".dll");
Assembly loadedAssembly;
try
{
loadedAssembly = Assembly.LoadFile(path);
}
catch (Exception exc)
{
Exception exp = exc;
throw;
}
if (loadedAssembly != null)
{
return loadedAssembly;
}
else
{
return null;
}
}
public string SearchFolder { get; set; }
}

Базовый каркас готов. Не большое отступление чего же я собственно хотел добиться:

  • Еще раз изолированность от основного приложения
  • В любой момент загрузка и выгрузка плагина. Если к примеру плагин упал, мы его просто выгрузим и заново загрузим

Что же у нас в Main:

Для начала я загружая сборку PluginFramework в режиме только для рефлексии и получаю Type нашего базового класса для всех плагинов

Assembly frameworkReflect =
Assembly.ReflectionOnlyLoadFrom(Path.Combine(Environment.CurrentDirectory, "PluginFramework.dll"));
Type basePluginType = frameworkReflect.GetType("MyPluginSample.Framework.Plugin");

А теперь поиск по всем имеющимся папкам

string pathPlugins = Path.Combine(Environment.CurrentDirectory, "Plugins");
DirectoryInfo directoryPlugins = new DirectoryInfo(pathPlugins);
foreach (var dir in directoryPlugins.GetDirectories())
{
FileInfo dllFile = dir.GetFiles(dir.Name + ".dll").First();

И опять я загружаю сборку в режиме только для рефлексии, но уже с плагином и ищу класс который наследуется от базового класса Plugin. Что бы мы здесь могли загрузить сборку с плагином и сравним с базовым типом нам и пришлось в начале PluginFramework тоже загрузить только для рефлексии.

Assembly reflectionAsm = Assembly.ReflectionOnlyLoadFrom(dllFile.FullName);
Type typePlugin = reflectionAsm.GetTypes().First(t => t.IsSubclassOf(basePluginType));

Теперь можно приступить к созданию нового домена, загрузить в него все необходимое и запустить наш плагин на выполнение

AppDomain pluginDomain = AppDomain.CreateDomain(dir.Name + " plugin");
AssemblyHelper helper = (AssemblyHelper)pluginDomain.CreateInstanceAndUnwrap("PluginFramework", "MyPluginSample.Framework.AssemblyHelper");
helper.SearchFolder = dir.FullName;
Plugin plugin = (Plugin)pluginDomain.CreateInstanceAndUnwrap(reflectionAsm.FullName, typePlugin.FullName);
plugin.Initialize();
}

Вот и все! И напишем тестовый плагин для проверки

public class MyPlugin1: Plugin
{
public override void Initialize()
{
Console.WriteLine("Executing in Plugin 1. Domain Id: {0}", AppDomain.CurrentDomain.Id);
}
}

Структура каталогов следующая:

Ссылка на исходники PluginSystem.zip

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 

Комментарии

1.
63K
09 марта 2011 года
2k9software
26 / / 09.03.2011
+1 / -0
Мне нравитсяМне не нравится
15 апреля 2011, 00:15:40
Добавьте ещё самообновление плагинов. Например с e-mail. Удобно и практично.

Сложной системе необходима структура (класс) описывающая плагин, его методы, входные и выходные данные. Это не просто реализовать, хотя вполне возможно.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог