Применение рефлексии для создания плагинов - Основное приложение
ОГЛАВЛЕНИЕ
Основное приложение
Приложение MainApp, к которому мы будем подключать плагины, это простое windows-forms приложение для отображения графический файлов. Оно реализует интерфейс IMainApp - класс формы определен как public class Form1 : System.Windows.Forms.Form, Interface.IMainApp. На форме находится PictureBox для вывода изображения. Для реализации интерфейса IMainApp определяем свойство Image для доступа к изображению.public Bitmap Image
{
get { return (Bitmap)pictureBox.Image; }
set { pictureBox.Image = value; }
}
В конструкторе формы вызывается метод FindPlugins, который находит плагины в папке с приложением и загружает их сборки. Для поиска и загрузки применяется рефлексия. Существует и другой подход - создать для приложения конфигурационный файл, в котором прописаны пути ко всем плагинам. При этом мы не сможем устанавливать плагины путем простого копирования сборок, что не есть хорошо.
void FindPlugins()
{
// папка с плагинами
string folder = System.AppDomain.CurrentDomain.BaseDirectory;
// dll-файлы в этой папке
string[] files = Directory.GetFiles(folder, "*.dll");
foreach (string file in files)
try
{
Assembly assembly = Assembly.LoadFile(file);
foreach (Type type in assembly.GetTypes())
{
Type iface = type.GetInterface("Interface.IPlugin");
if (iface != null)
{
Interface.IPlugin plugin = (Interface.IPlugin)Activator.CreateInstance(type);
plugins.Add(plugin.Name, plugin);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка загрузки плагина\n" + ex.Message);
}
}
Потенциальной проблемой для нашего кода может стать то, что из домена приложения нельзя выгрузить сборку. Если в папке с приложением окажется много сборок, которые будут загружаться в процессе поиска плагинов, то это приведет к ненужному расходу памяти. В таком случае можно создать новый домен, вызвав статическую функцию AppDomain.CreateDomain, загрузить все сборки в созданный домен и получить названия только тех сборок, которые содержат плагины, выгрузить домен функцией AppDomain(Unload) и загрузить сборки с плагинами в домен.
После того, как все плагины найдены, создаем для них в функции CreatePluginsMenu пункты меню. Названия пунктов меню берутся из ключей в хеш-таблице. Для обработки событий от меню для вызова плагинов создается обработчик OnPluginClick. В обработчике определяется названия пункта меню, который выбрал пользователь, и по нему, как по ключу в хеш-таблице, получаем интерфейс IPlugin соответствующего плагина. У плагина вызывается метод Transform, в качестве параметра this (т.к. класс формы наследуется от интерфейса IMainApp).
void CreatePluginsMenu()
{
// создаем обработчик для команд меню для плагинов
EventHandler handler = new EventHandler(OnPluginClick);
foreach (string name in plugins.Keys)
{
MenuItem item = new MenuItem(name, handler);
menuItemPlugins.MenuItems.Add(item);
}
}
private void OnPluginClick(object sender, EventArgs args)
{
Interface.IPlugin plugin = (Interface.IPlugin)plugins[((MenuItem)sender).Text];
plugin.Transform(this);
}