Laden...

Plugin DLL: XML Serialisieren / Assemblys Laden

Letzter Beitrag vor 15 Jahren 2 Posts 679 Views
Plugin DLL: XML Serialisieren / Assemblys Laden

Hallo liebes Forum,

ich hab für mein Programm eine Dll in Arbeit bei der ich nur eine all umfassende
Methode(RUN) an ein programm weiter gebe.
RUN hat dabei nur zwei wesentliche Variablen die für es wichtig sein dürften.
Diese wären ein String um in das Plugin Verzeichnis der Anwendung zukommen in die
es implementiert wird und ein booleescher wert der Angibt ob die Plugins überhaupt
gesucht und gefunden werden sollen von der Methode.

Es sollen dann in jedem weiteren Unterordner der Plugin Directory INDEX.XML - Dateien gefunden
werden die dann Aufschluss darüber geben wie das Plugin aufgebaut ist und was es überhaupt ist.

So sollte dann zum Beispiel ein Button der das Programm schliesst so geschrieben werden.


<Plugin>
<PluginName> CloseButton </PluginName> 'Beschreibender Name der in den Einstellungen
'aufgeführt werden soll um das Plug zu de-/aktivieren
'und als Name aufgeführt wird wenn anderweitig
'auf dieses Plugin zurückgegriffen wird
<Object>
System.Windows.Forms.Button
</Object> 'Klassenobjekt das deklariert welcher
'Steuerelementstyp erzeugt werden soll
<ObjectPropertys> 'Die Eigenschaften werden dann aus einer DLL
'gezogen die vom Plugin Schreiber erzeugt wurde
'so sollen darin size, Height, Width, Text,
'Background etc. in dieser Dll eindeutig beschrieben
'werden
Propertys.dll
</ObjectPropertys>
<ButtonClickEvent>
Closebutton_Click.dll
</ButtonClickEvent> 'beschreib einen für Button typischen Event(Click)
'Diese DLL sollte dann auch nur eine Methode
'beinhalten und zwar die des Buttonclickevent
</Plugin>

So sollte das INDEX.Xml ungefähr aufgebaut sein.
Ich suche in diesem zusammenhang auch nach einer Möglichkeit eine Xml-Struktur dann nach bestimmten Tags abzusuchen zB. Mit hilfe des XML-reader aber leider bin ich dort noch nicht auf etwas nützliches gestoßen weiss jemand vllt. wie das geht?
Wo ich leider auch schon zum erliegen komme ist das schreiben dieser Durchsuchfunktion die im
String nach INDEX.Xml's suchen soll kann mir da jemand nen Tipp geben?

Ich sag jetzt schon mal danke

ein Code ist nur so lange Spaghetti wie du keine Ahnung von ihm hast

Hallo Xx tja xX,

eine Sache habe ich nicht ganz verstanden, der boolische Wert der als Übergabeparameter an deine Emthode "RUN" gesendet wird ist mir nicht ganz klar. Ich vermute mal, das bei "true" die entsprechende *.DLL (Plugin) dann laden soll. Und beziehe daher auch den Lösungsansatz auf diese Vermutung:

Ein Weg wäre es die Informationen als Serialisierte Klasse zur verfügung zu stellen und wie folgt würde das ganze aussehen:

Zuerst erstellst du eine Klasse namens Serializer<T> diese generische Klasse dient als Standardserializer und ist eine reine Hilfsklasse. Innerhalb dieser Klasse erstellst du ein Proeprtie namens Path vom typ string, dann erstellst du zwei statsiche Methoden die wie folgt aussehen könnten:


		static internal void Save(string fileName, T obj)
			 {
				 this.FileName = fileName;

				 try
				 {
					 XmlSerializer serializer = new XmlSerializer(typeof(T));

					 using (TextWriter writer = new StreamWriter(fileName))
					 {
						 serializer.Serialize(writer, obj);
						 writer.Flush();
					 }
				 }
				 catch (Exception ex)
				 {
					 System.Diagnostics.Debug.Fail("Fehler beim Speichern der " + fileName + ex.Message);
					 throw;
				 }
			 }
		static internal T Load(string fileName)
		{
			try
			{
				T obj = null;
				using (TextReader reader = new StreamReader(fileName))
				{
					XmlSerializer serializer = new XmlSerializer(typeof(T));
					obj = serializer.Deserialize(reader);
				}
				return obj;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.Fail("Fehler beim Laden der " + fileName + ex.Message);
			}
			return null;
		}

Ebenso erstellst du noch zwei weitere public methoden, Load und Save, die ähnlich der Statischen Methoden aufgebaut sind, du musst legendlich den generischen typen dazu weglassen und auf this.GetType() umstellen.

Danach erstellst du eine Klasse Namens PluginInfo, diese besitzt als public Properties die entsprechenden Namen äquivalent zu den <NodeTags> deiner XML. Deine XML sollte folgende "wichtigen" Tags besitzen und somit deine Kalsse auch Properties:

string ClassName // klasse der Interfaceschnittstelle
string Location // Ort der Assembly (GAC/ABSOLUT)
string AssemblyName // Absoluter Pfad + <Assembly>.dll oder GAC

Der Klasse PluginInfo verpasst du dann das Attribut [Serializable()] diese erhällst du aus den Namespace von System.Xml.Serialization. und leitest PluginInfo von Serializer<T> ab, also PluginInfo : Serializer<PluginInfo>.

Im Anschluss erstellst du weiderum eine Klasse Namens AssemblyActivator, die wird als generischer Typ für das aktivierne von Assemblys verwendet und zeitgleich in das zu verwendende Interface gecastet. Du solltest grundsätzlich immer Factory für Plugins verwenden genauso wie Interfaces.

Diese stelle ich dir mal ganz rein, da es sonst zu komplex ist das niederzuschreiben:


  public enum AssemblyLocation
  {
    Gac, // Gloabl Assembly Cache
    AbsolutePath // Der Pfad ist in AssemblyName komplett angegeben
  }

  public class AssemblyActivator<T, I> : Helper.Serializer<T>
  {
    private AssemblyLocation _Location;

    public AssemblyLocation Location
    {
      get
      {
        return _Location;
      }
      set
      {
        _Location = value;
      }
    }

    private string _AssemblyName;
    public string AssemblyName
    {
      get
      {
        return _AssemblyName;
      }
      set
      {
        _AssemblyName = value;
      }
    }
    private string _ClassName;
    public string ClassName
    {
      get
      {
        return _ClassName;
      }
      set
      {
        _ClassName = value;
      }
    }

    internal I CreateObject()
    {
      Assembly a = null;
      string AssemblyPath = (Location == AssemblyLocation.CobraModule) ? 
        Cobra.APUtil.APModules.ModuleManager.ProgramDirectory + @"Module\" + AssemblyName : AssemblyName;
      try
      {
        if (Location != AssemblyLocation.Gac)
        {
          a = Assembly.LoadFrom(AssemblyPath);
        }
        else
        {
          a = Assembly.Load(AssemblyPath);
        }
      }
      catch (FileNotFoundException e)
      {
        MessageBox.Show(e.Message);
        return default(I);
      }
      Type ClassType;
      try
      {
        ClassType = a.GetType(ClassName);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Class " + ClassName + " not found in Assembly " + AssemblyName + " at " + AssemblyPath + "\r\n\r\n" + ex.Message);
        return default(I);
      }
      if (ClassType == null)
      {
        MessageBox.Show("Class " + ClassName + " not found in Assembly " + AssemblyName + " at " + AssemblyPath);
        return default(I);
      }

      return (I)Activator.CreateInstance(ClassType);
    }
  }
}

Deine Methode Run solltest du dann wie folgt erstellen:
public static PluginInfo RUN(string path, bool initalize){};

In der Methode Run rufst du den über den Parameter path nun über PluginInfo.Load(path die Plugininfo ab. Wenn der Parameter initalize = true ist dann holst du dir über den Assemblyactivator und anhand der PluginInfofiles (daher auch selber Namensraum der Porperties) über eine Schnittstelle die du noch erstellen musst (z.B. IPlugin) das Plugin ab und ladest es somit hinein. Der activator kann dann schon automatisch anhand der Konfiguration des XML Files festellen ob die assembly im global assemblycache zu suchen ist oder ob sie irgendwo anders liegt.
Als Rückgabewert gibst dann das PluginInfo zurück, somit kann jeder der von aussen auf deine RUN Methode drauf zugreift gleich die Assembly identifizieren.

Wenn den AssemblyActivator noch mit der PluginInfo Klasse verbindest wäre das noch schöner, das sähe ca. wie folgt aus:
PluginInfo : AssemblyActivator<Serializer<PluginInfo>>,IPlugin>

Damit könnte man dann zugleich in der IPluginInfo auch die Assembly von aussen her reinladen falls sie noch nicht geladen wurde.

Wenn das zu komplex sein sollte siehe Stichwörter:
System.Xml.Serialization.IXmlSerializable
Serialize

/PS

Einen kleinen gefallen Bitte - bennen den Titel des Beitrags noch um in z.B. "[Offen] XML Serialisieren / Assemblys Laden".

Ansonsten schönes Wochenende 🙂

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(