Страница 2 из 3 Основное приложение Приложение 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); } } Вначале определяется папка для поиска плагинов. Т.к. у нас все плагины лежат в одной папке вместе с основным приложением, то мы используем свойство BaseDirectory для домена нашего приложения. Затем получаем все dll файлы из папки - их массив возвращает статическая функция GetFiles. Сборку для проверки на наличие плагина загружаем методом LoadFile и в цикле проходим по всем типам, определенным в сборке. Если тип содержит интерфейс IPlugin (при этом метод GetInterface возвращает не null), то создаем экземпляр этого типа (инстанцируем) методом Activator.CreateInstance. Для последующего использования мы сохраняем инстанцированный тип в хеш-таблице plugins. Ключем в хеш-таблице является название плагина. Потенциальной проблемой для нашего кода может стать то, что из домена приложения нельзя выгрузить сборку. Если в папке с приложением окажется много сборок, которые будут загружаться в процессе поиска плагинов, то это приведет к ненужному расходу памяти. В таком случае можно создать новый домен, вызвав статическую функцию 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); } Окончательно приложение с загруженной фотографией выглядит следующим образом. В пункте меню Фильтры добавлены два плагина. |