Создание приложения с поддержкой плагинов

В этой статье описан пример, как создать приложение с поддержкой плагин-архитектуры.
Итак, создает новый проект (Приложение Windows Forms), назовем его «PluginDemo«. После создания проекта переименовываем форму в «FrmMain«, бросаем на форму «ListView«, обзываем его «lvPluginsList«, устанавливаем свойство «View» в «Details«, добавляем 3 колонки (столбца): «Имя плагина», «Описание», «Версия». Думаю, уже догадались, что в этом «ListView» будут выводиться подключенные плагины.
Теперь приступим к написаю интерфейса для будущих плагинов (должны же мы знать, как с ними «общаться»). В обозревателе решений клацаем правой кнопкой мыши по решению «PluginDemo«, выбираем «Добавить» => «Создать проект«.
В списке выбираем «Библиотека классов«, указываем имя — «PlugIn«. Переименовываем «Class1.cs» в «Interfaces.cs» . Открываем этот файл, убираем пустой класс, который создался автоматически и добавляем:
public interface IPlugin
{
string PluginName { get; } // имя плагина
string DisplayPluginName { get; } // имя плагина, которое отображается
string PluginDescription { get; } // описание плагина
string Author { get; } // имя автора
int Version { get; } // версия
IPluginHost Host { get; set; } // ссылка на главную форму
void Show(); // отображает форму
}
public interface IPluginHost
{
bool Register( IPlugin plug );
}
У всех плагинов должен быть реализован интерфейс, который мы создали выше.
При загрузке плагинов и добавлении их в список мы будет получать значения следующих полей плагина: «DisplayPluginName«, «PluginDescription» и «Version«.
Когда пользователь будет выбирать в списке плагинов нужный ему плагин, то программа будет вызывать метод «Show()» выбранного плагина, в нем плагин может показать свою форму и т.п.
Кстати, не забудьте добавить наследование интерфейса «IPluginHost» главной форме:
public partial class FrmMain : Form, IPluginHost
Также нужно реализовать метод «Register( IPlugin plug )«:
public bool Register( IPlugin plug )
{
return true;
}
Приступим к написанию плагинов. Снова добавьте новый проект («Библиотека классов«), назовите ее «Plugin1«. После создания проекта нажмите правой клавишей на «References«, выберите «Добавить ссылку…«, в открывшемся окне справа выберите «Решение» => «Проекты«, потом поставьте галочку напротив «PlugIn«.

Добавили мы ссылку на нашу библиотеку, где хранится интерфейс для наших плагинов. Нужно этот интерфейс реализовать в первом плагине (и остальных, естественно), но немного позже. Сейчас нужно добавить форму первому плагину. Снова, в обозревателе решений, нажимаем правой клавишей на «Plugin1«, выбираем «Добавить» => «Форма Windows«, называем ее «FrmP1Main«. Бросаем на форму «TextBox«, обзываем его «tbInfo«, свойство «Multiline» переключаем в «True«.
Далее открываем код нашей формы, добавляем:
using PlugIn;
Нашему классу «frmP1Main» добавляем поле «plug» типа «IPlugin«:
IPlugin _plug;
Конструктору нашей формы добавляем параметр «IPlugin plug» и в теле конструктора присваиваем параметр полю «_plug», т.е. выйдет такое:
public FrmP1Main( IPlugin plug )
{
InitializeComponent();
this._plug = plug;
}
Теперь добавляем обработчик события «Load» нашей форме (нажимаем на форму, с окне «Свойства» нажимаем на значок молнии, два раза клацаем по пункту «Load«; либо двойной щелчок на форме):
private void FrmP1Main_Load( object sender, EventArgs e )
{
this.tbInfo.AppendText( this._plug.Author + "\r\n" );
this.tbInfo.AppendText( this._plug.DisplayPluginName + "\r\n" );
this.tbInfo.AppendText( this._plug.PluginDescription + "\r\n" );
this.tbInfo.AppendText( this._plug.PluginName + "\r\n" );
this.tbInfo.AppendText( this._plug.Version + "\r\n" );
}
Думаю, все понятно: во время загрузки формы в «tbInfo» будет добавлена информация о плагине.
Настало время реализовать интерфейс в плагине. Переименовываем файл первого плагина «Class1.cs» в «PlugIn«. Удаляем пустой класс и добавляем следующий код:
public class PlugIn : IPlugin
{
private string _PluginName = "Plugin1";
private string _DisplayPluginName = "Первый плагин";
private string _PluginDescription = "Описание первого плагина";
private string _Author = "Dev";
private int _Version = 100;
IPluginHost _Host;
public void Show()
{
frmP1Main frm = new frmP1Main( this );
frm.ShowDialog();
}
public IPluginHost Host
{
get { return _Host; }
set
{
_Host = value;
_Host.Register( this );
}
}
public string PluginName
{
get
{
return _PluginName;
}
}
public string DisplayPluginName
{
get
{
return _DisplayPluginName;
}
}
public string PluginDescription
{
get
{
return _PluginDescription;
}
}
public string Author
{
get
{
return _Author;
}
}
public int Version
{
get
{
return _Version;
}
}
}
В коде выше реализованы методы и поля интерфейса «IPlugin«, также добавлены свои поля.
Надеюсь, еще не забыли о том, что когда пользователь будет выбирать нужный ему плагин в главном приложении, то наше приложение будет вызывать метод «Show()«, в котором создается экземпляр формы плагина и показывается пользователю.
Второй плагин попробуйте реализовать сами (в конце статьи есть исходник :)).
Возвратимся к главному приложению (PluginDemo). Плагины уже есть, а вот их загрузки нет Сейчас исправим. Добавьте главному приложению ссылку на «PlugIn» (выше есть пример; также подключите с помощью using), далее добавьте поле «_plugins«:
List<IPlugin> _plugins;
Создадим метод «LoadPlugins( string path )«, в качестве параметра будет передаваться путь к папке, где находятся наши плагины (у нас они будут находиться в папке с программой):
private void LoadPlugins( string path )
{
string[] pluginFiles = Directory.GetFiles( path, "*.dll" );
this._plugins = new List<IPlugin>();
foreach ( string pluginPath in pluginFiles )
{
Type objType = null;
try
{
// пытаемся загрузить библиотеку
Assembly assembly = Assembly.LoadFrom( pluginPath );
if ( assembly != null )
{
objType = assembly.GetType( Path.GetFileNameWithoutExtension( pluginPath ) + ".PlugIn" );
}
}
catch
{
continue;
}
try
{
if ( objType != null )
{
this._plugins.Add( (IPlugin)Activator.CreateInstance( objType ) );
this._plugins[ this._plugins.Count - 1 ].Host = this;
}
}
catch
{
continue;
}
}
}
Собственно, ищем все .dll в папке с программой (проверяя на наличие нашего «PlugIn«) и добавляем в список.
Теперь добавим метод, который будет добавлять подключенные плагины в «ListView«:
private void AddPluginsItems()
{
this.lvPlugins.Items.Clear();
foreach ( IPlugin plugin in this._plugins )
{
if ( plugin == null )
{
continue;
}
this.lvPlugins.Items.Add( plugin.DisplayPluginName );
this.lvPlugins.Items[ this.lvPlugins.Items.Count - 1 ].SubItems.Add( plugin.Version.ToString() );
this.lvPlugins.Items[ this.lvPlugins.Items.Count - 1 ].SubItems.Add( plugin.Author );
}
}
Пробегаемся по списку плагинов и добавляем в «lvPlugins» имя, версию и автора плагинов.
Добавляем методы «LoadPlugins()» и «AddPluginsItems()» в обработчик события «Load» формы:
private void FrmMain_Load( object sender, EventArgs e )
{
this.LoadPlugins( Application.StartupPath );
this.AddPluginsItems();
}
Добавляем еще один обработчик события «DoubleClick» для «lvPlugins«:
private void lvPlugins_DoubleClick( object sender, EventArgs e )
{
if ( this.lvPlugins.SelectedItems.Count > 0 )
{
int selectedIndex = this.lvPlugins.SelectedItems[ 0 ].Index;
this._plugins[ selectedIndex ].Show();
}
}
Здесь мы проверяем: есть ли выделенные элементы, если есть — получаем индекс первого выделенного и вызываем метод «Show()» выбранного плагина.
В общем, всё
Должно получится что-то типа такого:

Исходник:
Источник: https://www.softez.pp.ua/