AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Plugin-System Version 3
Thema durchsuchen
Ansicht
Themen-Optionen

Plugin-System Version 3

Ein Thema von Henning der zweite · begonnen am 19. Jul 2009 · letzter Beitrag vom 15. Mai 2011
Antwort Antwort
Seite 2 von 3     12 3      
Henning der zweite
Registriert seit: 10. Mär 2008
Hallo!

In diesem Thread stelle ich euch das

Plugin-System V. 3.1.0


vor, und ich muss sagen, dass es mir sehr viel Denkarbeit gekostet hat.
Und Denkarbeit ist schwierig - darum hat das Denken letztendlich mehrere Wochen gebraucht, das implementieren dann aber nur 2 Tage.

Crosspost bei Delphi-Treff
Crosspost bei Delphi-Forum

Ach ja: zwischendurch gab es auch das Plugin-System Version 2.
Bei dieser Version hat mir die Interface-Referenzzählung aber einen gewaltigen Strich durch die Rechnung gezogen, darum habe ich es verworfen.
Das Plugin-System Version 1 findet ihr bei Delphi-Treff, ich werde es aber hier nicht verlinken, da es veraltet ist und ich es nicht mehr unterstütze.

WICHTIG:
Bei Unklarheiten bitte sofort Fragen - egal wie dumm die Fragen sind!

Allgemeine Informationen
Entwickler: Henning Dieterichs
Lizenz: Dieses System kann frei verwendet werden, ich würde mich aber darüber freuen, wenn ich bei Benutzung von diesem Plugin-System namentlich erwähnt werde!
Getestete Umgebung: Windows XP, Delphi 7 (mit früheren Version nicht getestet!).
Benötigte Fremdkomponenten: Keine.

Download
Ganz kurz vorweg: Download gibts hier (mit 9 Demos).
Größe ca. 100 KiB.

Hier gibts das ganze mit bereits kompilierten DLLs und Exen.
Größe ca. 3,5 MiB.

Alle benötigten Dateien sind jeweils im Archiv enthalten

Wichtigste Features (Insider-Wissen)
  • Client <-> Host und Client <-> Client Datenaustausch möglich
  • Sprachunabhängig
  • Objekt-Export
  • Vererbung, Überschreibung exportierter Objekte
  • Klassen-Export, Klassen-Methoden
  • Flexible Plugin-Quelle durch Plugin-Packages (z.B. aus DLLs)

Was macht dieses Plugin-System?
Dieses Plugin-System ist dazu dar, ein Programm zu schreiben, welches nachher beliebig erweitert werden kann.
Dabei können vorhandene Module überschrieben, erweitert und verbessert werden, ohne das an ihnen etwas verändert werden muss.

Für wen ist dieses Plugin-System geeignet?
Im Grunde genommen für jeden, der es sich zutraut, sich mit den Interfaces von Delphi rumzuschlagen - der Einstieg ist sehr kompliziert, das Ergebnis dafür aber um so besser.
Dieses Plugin-System kann für kleinere Programme als auch für größere verwendet werden - auch wenn das System nicht ganz ausgenutzt wird.
Dadurch können fremde Programme durch andere Entwickler spielend leicht verbessert oder nach ihren Wünschen angepasst werden.
So kann es beispielsweise ein Plugin geben, welches einem Programm ein Tray-Icon hinzufügt - dabei spielt es letzendlich keine Rolle, um welches Programm es sich handelt, solange es mit dem Plugin-System ausgestattet ist.

Wie kann das funktionieren?
Ganz einfach - über Interfaces.
Die gesamte Kommunikation innerhalb des Plugin-Systems baut nur auf Interfaces auf, weshalb das gesamte System sogar Programmiersprachen-Unabhängig ist.

Allerdings besitzen die Interfaces von Delphi eine sehr unangenehme Eigenschaft: die Referenzzählung.
Warum die so blöd ist, kann
hier und hier nachgelesen werden.

Das Problem habe ich letztendlich mit Pointer auf Interfaces in den Griff bekommen - bei ihnen ignoriert Delphi die Referenzzählung.
Allerdings muss dazu einiges beachtet werden:
Die PInterfaces (Pointer auf Interfaces) dürfen nicht (bzw. wenn, dann nur, wenn man weiß, was man tut) dereferenziert werden.
Wenn ein PInterface in ein anderes gewandelt werden soll, kann die Klasse TIntf dazu benutzt werden - sie verwaltet das dann.

Aufbau des Plugin-Systems


Begriffe
Damit die Erklärung einfacher wird, habe ich mir einige Begriffe einfallen lassen, bzw. sie mit einer Bedeutung belegt:

Plugin: Das Plugin ist die kleinste Einheit: es stellt ein Objekt bzw. seine Klasse dar
Package:Ein Package ist eine Ansammlung von Plugins
Host: Der Host ist das Modul, das die Packages verwaltet.
Client: Der Client ist das Gegenstück und stellt ein Package dar, die Multiplizität zum Host beträgt N:1
Library: Der Host verwaltet alle Packages in einer gemeinsamen Library (= Bücherei)

Units
Das Plugin-System besteht hauptsächlich aus 4 Units:
plgHeader (die wichtigste Unit)
plgCore (enthält allgemeine Klasse)
plgLibrary (für den Host)
plgPackage (für den Client)

Client (als DLL)
- Erstellen eines Plugins

Erst ein Plugin enthält ausführbaren Code und ist somit das wichtigste Element in diesem System.

-- Festlegen des Interfaces
Zuerst muss das Interface festgelegt werden, welches das Plugin implementiert. Jedes Interface muss eine GUID besitzen.
Ein Plugin kann auch mehrere Interfaces implementieren.

Beispiel:
Delphi-Quellcode:
IDemo = interface
  ['{AD1D916F-6AD4-4221-BBCC-1960A30110DB}']
  procedure WriteSth;
end;
-- Implementieren des Interfaces
Nachdem die zu implementierenden Interfaces festgelegt wurden, können sie von einem oder mehreren Plugins implementiert werden.
Für jedes Plugin wird dazu eine neue Klasse erstellt, die von der Klasse TBasePlugin und dem Interface erbt.

Beispiel:
Delphi-Quellcode:
TDemo = class(TBasePlugin, IDemo)
public
  procedure WriteSth;
end;
{ TDemo }
procedure TDemo.WriteSth;
begin
  WriteLn('Hello World!');
end;
Das erste Plugin ist fertig - jetzt muss es nur noch in das Package eingebunden werden.

- Zusammenstellung des Packages
-- Einbinden der Package-Unit
Damit das Package benutzt werden kann, muss erstmal die Unit plgPackage eingebunden werden.
Diese Unit verwaltet ein Package-Objekt und exportiert automatisch eine Funktion, die das Package-Objekt exportiert.

Beispiel:
Delphi-Quellcode:
uses
  [...], plgPackage, [...];
-- Konfigurieren des Packages
Der Host sollte wissen, mit welchem Package er es zu tun hat.
Es hängt aber auch nur vom Host ab, ob er unkonfigurierte Clients zulässt.

Es ist aber trotzdem immer besser, ein Package mit einer Version, Reversion und einer eindeutigen GUID zu versehen, damit es identifiziert werden kann.
Außerdem sollte auch der Autor angegeben werden.

Beispiel:
Delphi-Quellcode:
  PluginPackage.Version := 1;
  PluginPackage.Reversion := 0;
  PluginPackage.Author := 'Henning';
  PluginPackage.GUID := StringToGUID('{65FE93FD-3E7C-464F-9A95-1B66EEE3557C}');
Hinweis: eine eindeutige GUID kann in Delphi mit der Tastenkombination Strg+Shift+G erzeugt werden.

-- Befüllen des Packages mit Plugins
Anschließend kann das Package mit Plugins befüllt werden.
Dazu wird zu dem Plugin eine Plugin-Klasse erzeugt, die die Informationen zu dem Plugin bereithält.
Diese sind einmal der Namespace und Name des Plugins.
Der Namespace bestimmt die Gültigkeit des Namens - zwei Plugins mit selben Namen aber unterschiedlichen Namespaces gehören nicht zusammen.
Der Namespace verringert also die Gefahr von unbeabsichtigten Namenskonflikten.
Dann muss jedem Plugin ebenfalls eine eindeutige GUID zugeordnet werden, sodass es innerhalb des Plugin-Systems identifizierbar bleibt.
Der Parameter Data (der letzte Parameter, im unten stehendem Beispiel nicht angegeben) ist optional und kann weggelassen werden.
Beispiel:
  PluginPackage.AddPluginClass(TCustomPluginClass.Create(TDemo, 'demo', 'demo1', StringToGUID('{11139406-15BF-4769-87B6-5195A0AC6020}'))); Hinweis: es können beliebig viele Plugins zu einem Package zusammengefasst werden.

- Fertig! Der Client ist jetzt eingerichtet!
Die DLL kann jetzt vom Host geladen werden.

Host
- Festlegen der Interfaces
Damit der Host die Plugins bedienen kann, müssen ihm die implementierten Interfaces bekannt sein.
Sie können aber so abstrakt definiert sein, dass sie eine große Möglichkeit an Erweiterungen bieten.

- Erzeugen der PluginLibrary
Damit der Host PluginPackages laden kann, benötigt er erstmal eine PluginLibrary.
Diese ist in der Unit plgLibrary enthalten und muss manuell erzeugt und am Ende wieder freigegeben werden.

Beispiel:
Delphi-Quellcode:
uses
  [...], plgLibrary, [...];
[...]
var
  PluginLib: TPluginLibrary;
begin
  PluginLib := TPluginLibrary.Create;
  try
    [...]
  finally
    PluginLib .Free;
  end;
end;
- Einbinden der Packages
Schon jetzt können die Packages der Clients eingebunden werden.

Beispiel:
Delphi-Quellcode:
[...]
try
  PluginLib.AddPackage(TDLLLibPlgPackage.Create('projClient.dll', PluginLib));
finally
[...]
Dabei wird eine Klasse erzeugt, die das Package aus der DLL wrappt.
Auf diese Weise ist es auch Möglich, die Packages aus anderen Quellen zu beziehen (weiteres in den Demos).

- Benutzung der darin enthaltenen Plugins
Jetzt geht es los - die Plugin-Klasse kann herausgesucht und erzeugt werden! Dabei kommen jetzt die PInterfaces ins spiel, also Vorsicht!

Beispiel:
Delphi-Quellcode:
var
  DemoObj : TIntf;
[...]
  DemoObj := TIntf.ApplyPIntf(PluginLib.PlgClasses['demo', 'demo1'].CreatePlugin([]));
[...]
Um jetzt das Interface aus diesem DemoObj zu erhalten muss erstmal eine weitere Variable angelegt werden.

Beispiel:
Delphi-Quellcode:
var
  PIDemoIntf: ^IDemo; //Pointer auf das Interface IDemo
[...]
Über DemoObj kann diese Variable befüllt werden:
Delphi-Quellcode:
  [...]
  DemoObj.GetPIntf(IDemo, PIDemoIntf);
  [...]
Jetzt kann die Funktion WriteSth aufgerufen werden:
Delphi-Quellcode:
  [...]
  PIDemoIntf.WriteSth;
  [...]
Und zum Schluss darf nicht vergessen werden, das Demo-Plugin wieder freizugeben:
Delphi-Quellcode:
  [...]
  DemoObj.Free;
  [...]
Fertig! Das erste Plugin-System wurde benutzt und kann jetzt auch mit viel komplexeren Plugins bestückt werden! (siehe Demos)


Wie am Anfang gesagt:
Es ist ganz wichtig, das ihr, falls ihr Fragen habt, fragt!
Also kurz gesagt:
Bei Fragen: fragen!

In den Demos wird auch noch vieles klarer und verständlicher.
Sie sind nach ihrem Schwierigkeitsgrad geordnet und die letzten beiden enthalten nochmal (fast) alles zusammen.
 
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#11
  Alt 21. Jul 2009, 14:55
im Grunde mußt du alle nötigen deiner eingebundenen Dateien mitgeben

und bei Verwendung von Fremdcode dieses mit erwähnen, oder wenn es die Lizenz zuläßt und es etwas ist, was nicht sooo viele verwenden, dann dieses eventuell auch mit dazupacken.

bei der Verwendung von externen Packages sollten diese auch mitgegeben werden, da ja nicht jeder überall ein Delphi installiert und somit diese Dateien eventuell auch in seinem System hat.

auch sollten Minimumanforderungen von Codes/Programmen genannt werden (soweit bekannt)
z.B.
- welche Delphi-Version mindestens zum Kompilieren ist
- welche Fremdkomponenten bzw. zusätzliche Programme installiert sein müssen
- welches Betriebssystem ist nötig

...

kurz um: alles was nicht standardmäßig auf einem "fast leerem" System vorhanden ist
  Mit Zitat antworten Zitat
Henning der zweite

 
Delphi 5 Standard
 
#12
  Alt 21. Jul 2009, 21:31
Neue Version: 3.1.0!

Changelog:
  • Ein Plugin kann jetzt mehrere Interfaces implementieren. Um zu prüfen, ob ein Plugin ein Interface unterstüzt, kann die Funktion "Supports" der Klasse TPluginClass benutzt werden.
  • LifeTimeTasks: Mithilfe dieser Klasse können (beim Client) Initialisierungs bzw. Finalisierungsaufgaben durchgeführt werden, wie z.B. das Setzten des Handles der Application. Näheres dazu in den Demos
  • Zwei neue Demos (eins über die LifeTimeTasks und das andere über eine Implementierung einer Factory-Class)
  • Hinzufügung aller erforderlichen Units

Leider ist das neue Pluginsystem inkompatibel mit dem alten - da aber niemand das neue benutzen konnte (aufgrund der fehlenden Units), ists quasi egal
Es wurde inkompatibel, weil ich am internen Aufbau eine Funktion ändern musste.
Henning D.
  Mit Zitat antworten Zitat
EugenB

 
Lazarus
 
#13
  Alt 23. Jul 2009, 16:45
Ist es kompatibel zu FPC? also kann man es mit FPC laufen lassen?
  Mit Zitat antworten Zitat
Henning der zweite

 
Delphi 5 Standard
 
#14
  Alt 23. Jul 2009, 16:54
Wenn du mit FPC den Free Pascal Compiler meinst:
Kompatibel ist es bestimmt - zumindest mit der Win32 Version.
Nur ob es kompilieren wird, weiß ich nicht! (Ich habe Free Pascal nicht)

Das Plugin-System ist kompatibel mit allen gängigen 32 Bit Compilern, die Interfaces und DLLs unterstützen, da diese nicht Delphi-Abhängig sind.
Nur die Implementation und die Header müssen übersetzt werden
Henning D.
  Mit Zitat antworten Zitat
Benutzerbild von Lumpiluk
Lumpiluk
 
#15
  Alt 23. Nov 2009, 19:38
Ich "arbeite" gerade mit 2 Leuten an einem kleinen Projekt und dabei war uns dein Pluginsystem bis jetzt sehr hilfreich!

Allerdings wollten wir jetzt ein OpenGL-Spiel einbinden, woraufhin der Bereich für das Plugin leider komplett schwarz geblieben ist...
Mit einem zusätzlichen Panel auf der Plugin-Form und dessen Handle hat es auch nicht funktioniert.
Gäbe es da eine Möglichkeit oder müssten wir das Spiel doch alternativ per ShellExecute im Vollbild laden?
  Mit Zitat antworten Zitat
Henning der zweite

 
Delphi 5 Standard
 
#16
  Alt 23. Nov 2009, 20:29
Zitat:
dabei war uns dein Pluginsystem bis jetzt sehr hilfreich!
Freut mich sehr zu hören!

Zitat:
Mit einem zusätzlichen Panel auf der Plugin-Form und dessen Handle hat es auch nicht funktioniert.
Eigentlich sollte das so funktionieren.

Probiere mal folgendes:
1. Möglichkeit: (müsste die sein, die du schon ausprobiert hast)
Erstelle ein Panel auf dem Hauptformular und veröffentliche ein Interface über das Plugin-System, mit dem das Handle des Panels
abgefragt werden kann.
Beim Initialisieren des OpenGL-Kontexts muss dann das Handle des Panels angegeben werden, welches über das veröffentlichte Interface abgefragt werden kann.
Über diesen OpenGL-Kontext müsstest du nun auf das Panel zeichnen können.

2. Möglichkeit:
Erstelle in dem Plugin per CreateParented ein Formular auf die Hauptanwendung (siehe Demo "GUI").
Da dieses Formular im Plugin erzeugt wurde, kann es direkt angesprochen werden.
Der Rest ist wie bei einer "normalen" OpenGL Anwendung.

P.S:
Mit OpenGL kenne ich mich leider nicht so gut aus, darum alle Angaben (wie immer) ohne Gewehr!
Henning D.
  Mit Zitat antworten Zitat
Jonny-The-XIV
 
#17
  Alt 29. Dez 2009, 15:29
Ich habe das problem gefunden
meistens wird application.OnIdle zum aufruf der renderfunktion von opengl verwendet. Das geht ja nicht in Plugins
aber mit nem timer ist alles io
  Mit Zitat antworten Zitat
Benutzerbild von MyRealName
MyRealName

 
Delphi 10.4 Sydney
 
#18
  Alt 29. Dez 2009, 16:01
Warum sollte es nicht in Plugins gehen ?
  Mit Zitat antworten Zitat
Benutzerbild von olee
olee

 
Turbo Delphi für Win32
 
#19
  Alt 29. Dez 2009, 16:12
Hi,

Es hat sich klasse angehört, was du gemacht hast...Nur...

wozu braucht man sowas?

Wieso kann man da nicht direkt Dlls benutzen und die über LoadLibary und Co. nutzen?
Björn Zeutzheim
  Mit Zitat antworten Zitat
Henning der zweite

 
Delphi 5 Standard
 
#20
  Alt 29. Dez 2009, 20:11
Zitat:
Wieso kann man da nicht direkt Dlls benutzen und die über LoadLibary und Co. nutzen?
Kann man auch, macht das Plugin-System ja auch intern.
Durch das Plugin-System ist es aber auch möglich bspw. vorhandene Funktionalität zu ersetzen und Plugins leichter zu verwalten.
(siehe "Features")

Kurzum: Wozu braucht man Delphi, wenn es doch Assembler gibt?
Henning D.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:32 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz