Недавно я нашел хороший пример для настройки модульного приложения и попытался адаптировать его к своим личным требованиям. Мое вдохновение пришло от этого вопроса здесь, на Stackoverflow:
Используя MEF с C #, как я могу вызывать методы на хосте из плагина?
Прежде всего ясам попробовал код из этого примера и все заработало. Я попытался изменить его для обработки нескольких плагинов, и это было успешно. Поэтому я попытался использовать этот код в своем собственном проекте, и вот тут-то и начались проблемы ...
Вот требования к моему проекту:
динамическая обработка нескольких плагинов,Чтобы установить новые плагины, просто поместите их библиотеку .dll в указанную папку.
запуск отдельных плагинов должен быть динамическим, а также. Они активируются по выбору пользователя
двунаправленный обмен данными и вызов методов: основное приложение должно иметь возможность вызывать определенные методы каждого плагина, а плагины должны иметь возможность вызыватьметод основного приложения для обратной связи в реальном времени о статусе плагина.
Вот фрагменты кода того, что я создал до сих пор (названия на немецком языке):
Интерфейс Библиотека:
using System;
using System.ComponentModel.Composition;
using Siemens.Engineering;
namespace Interfaces
{
[InheritedExport]
public interface IPlugin
{
void MeldungSenden();
void Programm(TiaPortal TIAInstanz, Project TIAProjekt);
string Name();
}
[InheritedExport]
public interface IMain
{
void MeldungEmpfangen(string[] Meldung, EventArgs e);
}
}
Один пример базового Плагин для целей тестирования:
using System;
using System.ComponentModel.Composition;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using Siemens.Engineering;
namespace FER_ServiceTool_Testmodul
{
[Export]
public class Program : Interfaces.IPlugin
{
static string[] TextMeldung = new string[8];
[Import(typeof(Interfaces.IMain))]
public Interfaces.IMain Hauptprogramm;
public string Name()
{
return "Testmodul 1";
}
public void MeldungSenden()
{
Hauptprogramm.MeldungEmpfangen(TextMeldung, null);
}
public void Programm(TiaPortal TIAInstanz, Project TIAProjekt)
{
TextMeldung[0] = "Ich bin eine Testmeldung";
TextMeldung[1] = "Testmodul 1";
TextMeldung[2] = "Testmeldung";
TextMeldung[3] = "Bausteine/FB";
TextMeldung[4] = "SPS_1";
TextMeldung[5] = "Generator";
TextMeldung[6] = "Programmbausteine/Maschinenhalle/Generator";
TextMeldung[7] = "Hier könnte ihre Korrektur stehen";
MeldungSenden();
}
}
}
Main *Приложение 1038 * имеет 2 класса, которые используются для плагина System:
Класс: Mainform: Form, Interfaces.IMain полностью настроен для [Export], поэтому плагины могут вызывать методы этого класса при необходимости. Класс имеет метод:
public void MeldungEmpfangen(string[] MeldungInput, EventArgs e)
{
MessageBox.Show("Empfange Meldung", "", MessageBoxButtons.OK);
EventHandler EintragenEvent = MeldungEintragen;
if(EintragenEvent != null)
{
MeldungText = MeldungInput;
EintragenEvent(this,e);
}
}
Этот метод получает обратную связь от плагинов и запускает событие для включения списка строк в DataGridView, расположенный в основной форме. Таким образом, я избегаю межпотоковой обработки управления
Второй класс основного приложения собирает файлы плагина .dll из указанной папки:
public class ServiceTool
{
private CompositionContainer _container;
[ImportMany(typeof(Interfaces.IPlugin))]
public List<Interfaces.IPlugin> Liste = new List<Interfaces.IPlugin>();
//const string ProfilPfad = @"D:\\Automation\\TIA\\FER-ServiceTool\\PlugIns";
public ServiceTool()
{
ModuleSuchen();
}
теперь япопробовал 2 РАЗНЫЕ реализации для метода "ModuleSuchen":
первая идет в соответствии с примером, который я перечислил в верхней части моего вопроса
void ModuleSuchen()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog("D:\\Automation\\TIA\\FER-ServiceTool\\PlugIns"));
catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
_container = new CompositionContainer(catalog);
try
{
this._container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}
Используя эту версию, мое приложение не можетчтобы загрузить любые файлы плагина .dll, и я понятия не имею, почему. Поэтому я попробовал другую реализацию:
void ModuleSuchen()
{
foreach(string Datei in Directory.GetFiles(ProfilPfad, "*.dll", SearchOption.TopDirectoryOnly))
{
try
{
Assembly.LoadFile(Datei);
}
catch
{
MessageBox.Show("Fehler", "", MessageBoxButtons.OK);
}
}
Type InterfaceTyp = typeof(Interfaces.IPlugin);
try
{
Type[] Typen = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(p => InterfaceTyp.IsAssignableFrom(p) && p.IsClass)
.ToArray();
foreach(Type Typ in Typen)
{
Liste.Add((Interfaces.IPlugin)Activator.CreateInstance(Typ));
}
}
catch(Exception e)
{
MessageBox.Show(""+e.ToString(), "", MessageBoxButtons.OK);
}
}
Используя этот метод, плагины загружаются правильно, но они не "видят" основное приложение. Поэтому я получаю исключение NullReferenceException в своем плагине, когда я пытаюсь использовать метод «MeldungSenden ()». Объект «Hauptprogramm» всегда равен нулю, в то время как он должен содержать что-то вроде «Mainform».
Я надеюсь, что вы можете сказать мне, где я допустил ошибку. Если вам нужна дополнительная информация, не стесняйтесь спрашивать.
Спасибо за терпение прочитать мой длинный вопрос