| Autor |
Beitrag |
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.01.08 13:44
Crossposting aus der Delphi-Praxis: www.delphipraxis.net...interfaces+in+c.html
Ich arbeite gerade mit dem Plugin-System Hydra von RemObjects. Unter Delphi hab eich es schon geschafft Daten mittels eines Interfaces auszutauschen zwischen Plugin und Host-Anwendung.
Jetzt versuche ich das gleiche unter C#. Leider sind da die Beispiele etwas mager. Mein bisheriger Versuch sieht so aus:
Plugin-Interface:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| using System; using System.Collections.Generic; using System.Text;
namespace HydraPluginHost { interface PluginInterface { string GetAuthor(); string GetVersion(); } } |
Plugin:
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:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using RemObjects.Hydra; using HydraPluginHost;
namespace HydraPlugin { [Plugin, VisualPlugin] public partial class HydraPlugin : VisualPlugin, PluginInterface { public HydraPlugin() { InitializeComponent(); }
public string GetAuthor() { return "Michael Puff"; }
public string GetVersion() { return "0.1 [Demo]"; } } } |
Laden des Plugin in der Host-anwendung:
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:
| private void lbPlugins_DoubleClick(object sender, EventArgs e) { try { if (lbPlugins.SelectedIndex == -1) return; if (lbPlugins.SelectedItem is PluginDescriptor) { currentPlugin = null; hostPanel1.UnhostPlugin(); currentPlugin = moduleManager1.CreateInstance(lbPlugins.SelectedItem as PluginDescriptor); hostPanel1.HostPlugin(currentPlugin as IBasePlugin); this.Text = (currentPlugin as PluginInterface).GetAuthor(); }
} catch (Exception ex) { MessageBox.Show(ex.Message); } } |
Compilieren tut er es ohne Fehler. Allerdings zur Laufzeit bekomme ich in der Zeile this.Text = (currentPlugin as PluginInterface).GetAuthor(); den Fehler:
| Zitat: | | Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. |
Ich müsste wohl erst eine Objekt-Instanz von PluginInterface erzeugen. Aber da stehe ich jetzt etwas auf dem Schlauch, wie ich das mache.
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 16.01.08 15:03
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.01.08 15:12
Besten Dank, auch wenn ich das noch nicht verstehe.
Hast du das irgendwie mit einem Assistenten gemacht? Einen Plugin Designer kann ich bei mir VS 2008 Beta) nirgends finden.
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 16.01.08 15:19
Hallo!
zum Plugin:
Hydra hat in meinem Visual Studio einen neuen Projekttyp angelegt namens "Plugin Module", den ich unter der Kategorie "RemObjects Hydra" finde, wenn ich ein neues Projekt anlegen will. Wenn ich ein solches Projekt anlege und diesem Projek über Rechtsklick -> Add -> New Item eine neue Datei hinzufügen will, kann ich dort aus verschiedenen Hydra-Items wählen, so auch dem Visual-Plugin. Für das gibt's dann auch den normalen WinForms-Designer.
Hostanwendung:
Ich lege eine normale WinForms-Anwendung an, in meiner Toolbox gibt es die Kategorie "RemObjects Hydra". Darin befindet sich der ModuleManager und das HostPanel. Die kann ich dann einfach auf die Form ziehen.
Grüße
Christian
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.01.08 15:26
Ja, so geht das mit dem VS auch. Der resultierende Plugin-Code sieht dann so aus:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using RemObjects.Hydra;
namespace PluginModule1 { [Plugin, VisualPlugin] public partial class Plugin1 : VisualPlugin { public Plugin1() { InitializeComponent(); } } } |
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 16.01.08 15:31
Da der eine partial Class anlegt, dürfte der Rest in einer anderen Datei stecken. Bei C# ist es ja üblich, den Designer-Code vom Rest zu trennen.
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.01.08 15:38
Ja, es gibt noch eine ModuleController.cs Datei:
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:
| namespace PluginModule1 { partial class TheModuleController { private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
#region Windows Form Designer generated code
private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.smallImages = new System.Windows.Forms.ImageList(this.components); this.largeImages = new System.Windows.Forms.ImageList(this.components); this.smallImages.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; this.smallImages.ImageSize = new System.Drawing.Size(16, 16); this.smallImages.TransparentColor = System.Drawing.Color.Transparent; this.largeImages.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; this.largeImages.ImageSize = new System.Drawing.Size(16, 16); this.largeImages.TransparentColor = System.Drawing.Color.Transparent; this.LargeImages = this.largeImages; this.SmallImages = this.smallImages;
}
#endregion
private System.Windows.Forms.ImageList smallImages; private System.Windows.Forms.ImageList largeImages; } } |
Ich habe das Laden jetzt mal so probiert:
C#-Quelltext 1: 2: 3: 4: 5:
| currentPlugin = moduleManager1.CreateInstance(lbPlugins.SelectedItem as PluginDescriptor); hostPanel1.HostPlugin(currentPlugin as IBasePlugin); IPluginInterface pluginInterface; pluginInterface = (IPluginInterface)moduleManager1.CreateInstance(lbPlugins.SelectedItem as PluginDescriptor); this.Text = pluginInterface.Caption(); |
Da bekomme ich die Fehlermeldung:
| Zitat: | | Das Objekt des Typs HydraPlugin.HydraPlugin kann nicht in Typ HydraPluginHost.IPluginInterface umgewandelt werden. |
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 16.01.08 15:50
Hm. Das ist komisch.
Wenn Du mal pluginInterface als object deklarierst, in die Zeile nach dem CreateInstance einen Breakpoint setzt und dann schaust, was in pluginInterface drin ist, wie sieht das dann aus?
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.01.08 15:54
Dann ist es null:
C#-Quelltext 1: 2:
| object pluginInterface; pluginInterface = (IPluginInterface)moduleManager1.CreateInstance(lbPlugins.SelectedItem as PluginDescriptor); |
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 16.01.08 15:57
Lass mal den Cast weg und schau, was dann zurück gegeben wird. Falls das auch Null ist, versuch mal, statt eines PluginDescriptors nur den Namen des Plugins (Format: Namespace.Klasse) als String zu übergeben.
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 16.01.08 16:10
Also, wenn ich den Cast weglasse:
C#-Quelltext 1: 2:
| object pluginInterface; pluginInterface = moduleManager1.CreateInstance(lbPlugins.SelectedItem as PluginDescriptor); |
Hab eich laut Debugger ein:
| Zitat: | | pluginInterface {HydraPlugin.HydraPlugin} object {HydraPlugin.HydraPlugin} |
da drin.
Und wenn ich nur den Namen des Plugins übergebe:
| Zitat: | | cannot convert from 'object' to 'RemObjects.Hydra.PluginDescriptor' |
Wenn wir das morgen noch nicht geschafft haben, schreibe ich mal den Support an. 
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 21.01.08 15:26
Das erste Problem wäre dank Christian gelöst. Man muss das Interface in ein Assembly verpacken und jenes dann referenzieren. Fügt man nur die c-Datei dem Projekt hinzu hat man zwei unterschiedliche Referenzen im Plugin- und Host-Code.
Ich stelle noch ein Demo hier ein. aber vorher muss noch ein anderes Problem gelöst werden und zwar die Kommunikation vom Host zum Plugin.
@Christian: Wenn ich es so mache, wie du es in der PN vorschlägst:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| namespace PluginInterface { public interface IPluginInterface { string Author(); string Caption(); string HostExePath { get; set; } } } |
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| public string HostExePath { get { return hostExePath; } set { hostExePath = value; } } |
Bekomme ich beim Laden der Plugins eine Fehlermeldung:
| Zitat: | | Mindestens ein Typ in der Assembly kann nicht geladen werden. Rufen Sie die LoaderExceptions-Eigenschaft ab, wenn Sie weitere Informationen benötigen. |
Ich hatte mir das auch eher so gedacht, dass ich in dem Plugin Assembly ein IHostInterface implementiere und dann dann ähnlich löse, wie mit dem IPluginInterface.
Hinzukommt, dass ich es noch irgendwie schaffen muss, dass ein Event ausgelöst wird, wenn das Plugin Daten an den Host übergibt.
|
|
Robert_G
      
Beiträge: 416
Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
|
Verfasst: Mo 21.01.08 16:23
Ich würde dir hier gerne weiterhelfen, aber du solltest als langjähriger Moderator selbst wissen, dass man ohne Infos keine Antworten geben kann.
Also: Was enthält LoaderExceptions?
Hast du beide Seiten neu kompiliert, nachdem du das Interface geändert hast?
Also mehr als so offensichtliche Dinge kann ich erstmal nicht fragen, für mehr fehlen die nötigen Details...
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 21.01.08 16:26
Robert_G hat folgendes geschrieben: | Ich würde dir hier gerne weiterhelfen, aber du solltest als langjähriger Moderator selbst wissen, dass man ohne Infos keine Antworten geben kann.
Also: Was enthält LoaderExceptions?
Hast du beide Seiten neu kompiliert, nachdem du das Interface geändert hast?
Also mehr als so offensichtliche Dinge kann ich erstmal nicht fragen, für mehr fehlen die nötigen Details... |
Jupp habe bneide Seiten kompiliert. Was LoadException enthält hab eich noch nicht rausfinden können. Jetzt ist aber auch erst mal Feierabend.
PS: Das Posting war auch als Feedback für Christian gedacht. 
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 23.01.08 09:50
Ich versuche jetzt einen anderen Weg zu gehen mit einem extra Interface für den Host, um Informationen vom Host an das Plugin zu übergeben.
Interface Assembly:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| namespace PluginInterface { public interface IPluginInterface { string Author(); string Caption(); }
public interface IHostInterface { string GetHostExepath(); } } |
Host Implementation:
C#-Quelltext 1: 2: 3: 4: 5:
| public string HostExepath() { return Application.ExecutablePath; } |
Aufruf im Plugin:
C#-Quelltext 1: 2: 3: 4: 5: 6:
| private void button1_Click(object sender, EventArgs e) { string s; s = (Host as IHostInterface).HostExepath(); label2.Text = s; } |
Leider bekomme ich beim Betätigen der Schaltfläche die Fehlermeldung:
| Zitat: | | Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. |
RemObjects Hydra gibt es auch für Delphi und da geht es genauso, wie ich es auch hier in C# versuche und da funktioniert es eben.
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 23.01.08 10:01
Wie genau hast Du das Interface beim Host eingebaut? Ich habe nicht so viel Erfahrung mit Hydra, aber der Host ist ja, denke ich, nicht die Form, sondern das HostPanel oder der ModuleManager.
Luckie hat folgendes geschrieben: | | RemObjects Hydra gibt es auch für Delphi und da geht es genauso, wie ich es auch hier in C# versuche und da funktioniert es eben. |
Du vergleichst Äpfel mit Birnen 
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 23.01.08 10:42
Wie ich es eingebaut habe? Ist nichts wildes:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| namespace HydraHost { public partial class Form1 : Form, IHostInterface { private IHYCrossPlatformPlugin currentPlugin;
public string HostExepath() { return Application.ExecutablePath; }
public Form1() { InitializeComponent(); ...; ...; |
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 23.01.08 13:56
Wie ich schon schrieb: Ich bezweifle, dass die Form der Host ist.
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 23.01.08 14:04
In der Host-Anwendung ist es dieses Hydra-Host-Panel, das stimmt schon. Das heißt, ich müsste das Interface im Kontext dieses Panels implementieren? Aber wie komme ich da ran?
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 23.01.08 14:20
Andere Idee:
Erweitere das IPluginInterface um Folgendes:
C#-Quelltext 1: 2: 3: 4:
| public interface IPluginInterface { Form1 theHostForm {get; set;} } |
Im Host kannst Du nach dem Laden des Plugins dann einfach den Host setzen:
C#-Quelltext 1:
| thePlugin.theHostForm = this; |
Und - tada! - Du kannst im Plugin jede public-Methode der Form benutzen 
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|