Plugin-Manager

Hi Experten,

ich will mir für meine Programme einen kleinen Plugin-Manager basteln!
Das ganze habe ich mir so vorgestellt:

Alle Plugin-Dateien(vorraussichtlich txt-Dateien) befinden sich in einem Ordner Plugins. Der Benutzer hat die Möglichkeit, selbst Plugins zu schreiben(in C# Syntax, halt nur in txt-Dateien). Diese werden immer an einer bestimmten Stelle im Quellcode ausgeführt(z.B. beim Start).

Das is dann auch schon die ganze Idee. Jetzt is aber die Frage, wie kriege ich mein Programm dazu, den Code, den ich aus den Textdateien lese, auzuführen? Vielleicht schreibe ich ihn in eine exe und starte sie?(bis jetzt mein einziger Ansatz) Könnt ihr mir da weiterhelfen?

MfG
Rodario

Das is dann auch schon die ganze Idee. Jetzt is aber die
Frage, wie kriege ich mein Programm dazu, den Code, den ich
aus den Textdateien lese, auzuführen? Vielleicht schreibe ich
ihn in eine exe und starte sie?(bis jetzt mein einziger
Ansatz) Könnt ihr mir da weiterhelfen?

So funktioniert das nicht, C# muss schließlich KOMPILIERT werden. C# zu verwenden macht daher an dieser Stelle nicht viel Sinn. Was du möchtest, ist eine Skriptsprache. IronPython (http://www.codeplex.com/Wiki/View.aspx?ProjectName=I…) wäre eine gute Idee. Da kannst du direkt wie in C# mit deinen .NET-Objekte umgehen, aber das Skript kann zur Laufzeit gelesen und ausgeführt werden.

Viele Grüße,
Sebastian

Hier noch ein netter Artikel zu IronPython, falls die IronPython-Seite zu unübersichtlich ist:

http://www.codeproject.com/KB/dotnet/ironpython.aspx

In deinem C#-Code steht dann einfach sowas wie:

// Scripting Engine erzeugen
pythonEngine = new PythonEngine();

// im Python-Skript ist das Form als „form“ ansprechbar:
pythonEngine.Globals.Add(„form“, this);

// Z.B. einzelnen Befehl ausführen:
pythonEngine.Execute(„form.Text = ‚Hallo‘“);

// Datei ausführen
string filename = „plugin1.py“;
pythonEngine.ExecuteFile(filename);

Grüße,
Sebastian

Hi Sebastian,

klingt super, werd ich mal ausprobieren, danke!

MfG
Rodario

Hallo!

Du musst übrigens keine explizite Scriptengine eines Drittherstellers einbinden - .NET hat alles an Bord, um selbst Quellcode (z.B. in C# oder VB.NET) zu compilieren und das Ergebnis auszuführen.

Die dafür nötigen Klassen findest Du im Namespace System.CodeDom bzw. System.CodeDom.Compiler.
Das Vorgehen ist dabei folgendes:

  • Du erzeugst Dir einen Compiler für die gewünschte Zielsprache
  • Diesen Compiler lässt Du den betreffenden Code (eben z.B. aus der txt-Datei) übersetzen
  • Wenn es dabei keine Fehler gab, erhältst Du eine (temporäre) Assembly
  • Aus dieser Assembly kannst Du dann mittels Reflection Objekte instanziieren und verwenden.

Üblicherweise definiert man sich ein oder mehrere Interfaces, welche die Objekte im Plugin implementieren müssen. Dann hat man einen Anhaltspunkt, welche Objekte man sich aus der temporären Assembly instanziieren kann.

Gruß,
Martin

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Du musst übrigens keine explizite Scriptengine eines
Drittherstellers einbinden

Ich glaube, Microsoft ist in dem Fall kein Dritthersteller. :smile:

  • .NET hat alles an Bord, um selbst
    Quellcode (z.B. in C# oder VB.NET) zu compilieren und das
    Ergebnis auszuführen.

Naja… nicht, dass ich das schon mal getan hätte, aber ich denke, dass es auf diese Art wesentlich komplexer wird (wegen Zwang zu Interface, Reflection erfordert einen gewissen Aufwand, etc). Aber interessant, dass man Assemblies auch im Speicher statt auf Platte erzeugen kann (das habe ich gerade ergoogelt). Dadurch rückt diese Methode immerhin in den Bereich des Möglichen.

Grüße,
Sebastian

Plugin Manager fertig
Es ist vollbracht, der Plugin Manager ist größtenteils fertig
*sich freu*

Danke für die viele Hilfe, ich habs jetzt mal mit System.CodeDom gelöst!

Für alle Interessierten hier mal der Quellcode:

using System;
using System.Collections;
using System.IO;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.CSharp;

namespace Plugin\_Manager
{
 public partial class Form1 : Form
 {
 public class Builder
 {
 public void Generate()
 {
 StringBuilder sb = new StringBuilder();
 ICodeCompiler compiler = new CSharpCodeProvider().CreateCompiler();
 CompilerParameters compArgs = new CompilerParameters();
 compArgs.GenerateExecutable = true;
 compArgs.OutputAssembly = "generiert.exe";
 compArgs.CompilerOptions = "/target:winexe";

 foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
 {
 compArgs.ReferencedAssemblies.Add(asm.Location);
 }

 string filename = Application.StartupPath + "\\Plugins\\plugin1.txt";
 StreamReader myFile = new StreamReader(filename, System.Text.Encoding.Default);

 string text;

 while((text = myFile.ReadLine()) != null)
 {
 sb.Append(text);
 }
 string Sourcecode;
 Sourcecode = sb.ToString();

 compiler.CompileAssemblyFromSource(compArgs, Sourcecode);
 }
 }
 public Form1()
 {
 InitializeComponent();
 }

 private void button1\_Click(object sender, EventArgs e)
 {
 Builder build = new Builder();
 build.Generate();
 System.Diagnostics.Process.Start(Application.StartupPath + "\\generiert.exe");

 }
 }
}

Auskommentiert hab ich ihn jetzt nicht, aber ich denke, dass ihr den auch so versteht.
Ist vielleicht nicht die optimale Lösung und ich bin für Vorschläge offen! Das nächste wird sein, dass er mehrere Plugins laden kann.

MfG
Rodario

Eine Frage eines Einsteigers zu dem Quellcode…

Für alle Interessierten hier mal der Quellcode:

namespace Plugin_Manager
{
public partial class Form1 : Form
{
public class Builder
{
public void Generate()
{

Ist es üblich / okay / ungewöhnlich, dass man eine Klasse (hier: Builder) innerhalb einer anderen Klasse deklariert?

Was passiert beim Instanzieren von Form1? Ist Builder dann ein „Aggregat“ von Form1? Oder wird das draußen gelassen?

Hallo,

Was passiert beim Instanzieren von Form1? Ist Builder dann ein
„Aggregat“ von Form1? Oder wird das draußen gelassen?

Stichwort „Nested Classes“ oder „inner classes“ für google.

genauso wie in Java.
Manchmal möchte man eine Klasse ausschließlich in einer anderen Klasse verwenden.

Gruss
Joey

Hi!
Prima, ein paar Vorschläge seien aber erlaubt:

  • Das Einlesen des Quelltextes ist furchtbar umständlich. Mach doch einfach folgendes:

    using (StreamReader sr = new StreamReader(filename))
    Sourcecode = sr.ReadToEnd();

  • Der Compiler kann auch eine temporäre Assembly erstellen. Dazu setzt Du GenerateInMemory in den CompilerParameters auf true (statt GenerateExecutable). Das CompileAssemblyFromSource liefert (neben einer Liste möglicherweise aufgetretener Compilefehler, die Du auch beachten solltest) auch die generierte Assembly.
    Z.B. mit deren Methode CreateInstance() kannst Du dann sofort Objekte aus ihr erzeugen und verwenden, ohne den Umweg über ein weiteres Executable gehen zu müssen.

Gruß,
Martin

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo,

Was passiert beim Instanzieren von Form1? Ist Builder dann ein
„Aggregat“ von Form1? Oder wird das draußen gelassen?

Stichwort „Nested Classes“ oder „inner classes“ für google.

genauso wie in Java.
Manchmal möchte man eine Klasse ausschließlich in einer
anderen Klasse verwenden.

Oh, es ist allerdings nicht ganz so wie bei Java. Innere Klassen in C# verhalten sich so wie statische innere Klassen in Java. Die Instanzen nicht-statischer innerer Klassen von Java existieren auch „innerhalb“ einer Instanz der Mutterklasse, d.h. sie können auf die nichtstatischen Methoden und die Felder zugreifen.

Grüße,
Sebastian