Autor Beitrag
J2T
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Mo 19.05.08 16:59 
Hallo Community,

Also ich möchte eine Pluginarchitekur entwickeln mit C#/.Net. Ich habe das schonmal vor einiger Zeit mit C++/Win32 API realisiert. Dort lief es wie folgt. Man erstellte sich ein Interface in einer Headerdatei und gab dies der DLL und dem ClientCode. Dann konnte der Clientcode zur Laufzeit die DLL laden und auch entscheiden welche konkrete Klasse/Object instanziert werden soll. Der Export einer konkreten Klasse des Interfaces wurde über C-Funktionen gelöst da nativ eine DLL nur Funktioen exportieren kann. Wahlweise konnte man den ganzen Lade-und Instanzierungsvorgang in eine statische .lib packen.
Vorteil das ganzen ist uA wie ihr sicherlich wisst, das bei Änderungnen in der Klasse nicht der ClientCode neukompiliert werden muss. Ausser es ändert sich das Interface.

So nun meine simple Frage: Wie wird sowas mit C#/.Net realisiert. Es gibt soweit ich weiß ja keine .h-Dateien und auch keine .libs oder?Ich gebe zu ich bin mit C# bzw. eigentlich dem .Net Framework nicht wirklich vertraut. Ich habe auch schon über die neue AddIn Architektur aus .net 3.5 gelesen. Jedoch war der Artikel net so wirklich prägnant und ich glaube auch dort muss die DLL schon zur LinkTime angeben werden. Ich weiß auch das es den Unmanaged Code gibt mit dem von der Seite aus gesehn die Rückgabe der entsprechenden Pointer kein Problem darstellen sollte.

Das System sollte:
- die DLL dynamisch zur Laufzeit laden können
- den Cast und die Pointerrückgabe in eine Art .lib auslagern können

Ich hoffe ihr versteht mein Anliegen und könnt mir helfen ;-)

btw für alle die sich das 'original' mal anschaune wollen:
www.gamedev.net/refe...icles/article928.asp
www.flipcode.com/arc...ur_Application.shtml
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 19.05.08 17:14 
:welcome:

Ich würde System.AddIn unbedingt einen zweiten Besuch abstatten, die API sollte wirklich alle Wünsche abdecken. Dieser MSDN-Artikel sieht ganz vernünftig aus, und im Vergleich zum Umfang von System.AddIn ist der prägnant ;) .
J2T Threadstarter
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Di 20.05.08 16:28 
Heho danke für deine Antwort. Habe mir den Link angeschaut und auch noch weitere Sachen zu dem Thema. Werde wohl erstmal das System von Hand umsetzten und wenn ich Zeit habe, werde auch nochmal die Basics von AddIn implementieren.

mfg
J2T
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Di 20.05.08 16:54 
Hi!

Folgender Code stammt aus einem Projekt von mir, welches schon eine ganze Weile zurückliegt. Er kann also noch Jugendsünden aus einr Zeit enthalten, wo ich in .NET noch nicht so firm war ;-)

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
  class PluginCollection
  {
    private List<Plugin> plugins;
    private int guiPluginCount;

        public PluginCollection(IHostForm Hostform, ListBox lb_plugins)
    {      
      string dirName = new FileInfo(Application.ExecutablePath).DirectoryName;      
      string[] files = Directory.GetFiles(dirName, "*.plugin.dll");

      plugins = new List<Plugin>(files.Length);
      
      foreach (string aFile in files)
                LoadPlugin(aFile, Hostform, lb_plugins);
    }

    public int GUIPluginCount
    {
      get
      {
        return guiPluginCount;
      }
    }

    public int PluginCount
    {
      get
      {
        return plugins.Count;
      }
    }

    private void LoadPlugin(string pluginName, IHostForm hostForm, ListBox lb_plugins)
    {
      if (File.Exists(pluginName))
      {
        Assembly plugAsm = Assembly.LoadFrom(pluginName);

        string pluginTitle = Path.GetFileName(pluginName);
        pluginTitle = pluginTitle.Substring(0, pluginTitle.IndexOf(".plugin.dll"));
        
                Type guiClass  = plugAsm.GetType("SharpPix.Plugins." + pluginTitle + ".PluginGUI");
        Type codeClass = plugAsm.GetType("SharpPix.Plugins." + pluginTitle + ".PluginCode");

                //Es muss mindestens eine Code-Klasse vorhanden sein.
        if (codeClass != null)
        {
          IGuestCode code = (IGuestCode)Activator.CreateInstance(codeClass);
          code.HostForm = hostForm;

                    //Eventuell vorhandene GUI instanzieren.
          UserControl gui = null;
          if (guiClass != null && guiClass.BaseType == typeof(UserControl))
          {
            gui = (UserControl)Activator.CreateInstance(guiClass);
            ((IGuestGUI)gui).HostForm = hostForm;
                        
            guiPluginCount++;
          }
                    Plugin p = new Plugin((IGuestGUI)gui, code);

                    if (p.Gui != null)
                        lb_plugins.Items.Add(p);

                    plugins.Add(p);
        }
      }
    }

    public IEnumerator<Plugin> GetEnumerator()
    {
      foreach (Plugin p in plugins)
      {
        yield return p;
      }
    }

  }


Zur Erklärung:

Die Klasse soll alle für die Anwendung gefundenen Plugins halten.

Ein Plugin besteht mindestens aus Code, kann aber auch eine GUI enthalten, muss den Namen "{PLUGIN}.plugin.dll" enden und im Ordner der Hauptanwendung liegen (erste Sünde :D).

Die Klasse für Code muss dann im Namespace "SharpPix.Plugins.{PLUGIN}" liegen (SharpPix war der Programmname), und den Namen "PluginCode" haben, die Klasse für GUI im selben Namespace liegen und entsprechend "PluginGUI" heißen. Der Code muss IGuestCode implementieren und die GUI muss von UserControl abgeleitet sein und IGuestGUI implementieren.

Die Hostform im Programm implementiert IHostForm, welches ganz viele Events bereitstellt, in die sich ein Plugin einklinken kann.

Die Plugins werden in der Liste in Form dieser Klasse gehalten:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
    public class Plugin
    {
        private IGuestGUI gui;

        /// <summary>
        /// Die GUI des Plugins
        /// </summary>
        public IGuestGUI Gui
        {
            get { return gui; }
            set { gui = value; }
        }

        private IGuestCode code;
        /// <summary>
        /// Der Code des Plugins
        /// </summary>
        public IGuestCode Code
        {
            get { return code; }
            set { code = value; }
        }

        /// <summary>
        /// Erzeugt ein neues Plugin
        /// </summary>
        /// <param name="Gui">GUI (von SharpPix instanziert)</param>
        /// <param name="Code">Code (von SharpPix instanziert)</param>
        /// <remarks>
        /// Setzt die Plugin-Property der übergebenen Gui- und Code-Instanz
        /// </remarks>
        public Plugin(IGuestGUI Gui, IGuestCode Code)
        {
            gui = Gui;
            code = Code;

            Gui.Plugin = this;
            Code.Plugin = this;
        }

        /// <summary>
        /// Die String-Repräsentation des Plugins
        /// </summary>
        /// <returns>Den Titel des Plugins</returns>
        public override string ToString()
        {
            return gui.Caption;
        }
    }


Ich hoffe, dass ich Dir damit ein paar Anregungen gegeben habe, wie man so etwas umsetzen kann :-)

Ach ja, damals gab es den System.AddIns-Namespace noch nicht :D

Grüße
Christian

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".