|
Antwort |
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)
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:
-- Implementieren des Interfaces
IDemo = interface
['{AD1D916F-6AD4-4221-BBCC-1960A30110DB}'] procedure WriteSth; end; 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:
Das erste Plugin ist fertig - jetzt muss es nur noch in das Package eingebunden werden.
TDemo = class(TBasePlugin, IDemo)
public procedure WriteSth; end; { TDemo } procedure TDemo.WriteSth; begin WriteLn('Hello World!'); end; - 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:
-- Konfigurieren des Packages
uses
[...], plgPackage, [...]; 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:
Hinweis: eine eindeutige GUID kann in Delphi mit der Tastenkombination Strg+Shift+G erzeugt werden.
PluginPackage.Version := 1;
PluginPackage.Reversion := 0; PluginPackage.Author := 'Henning'; PluginPackage.GUID := StringToGUID('{65FE93FD-3E7C-464F-9A95-1B66EEE3557C}'); -- 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:
- Einbinden der Packages
uses
[...], plgLibrary, [...]; [...] var PluginLib: TPluginLibrary; begin PluginLib := TPluginLibrary.Create; try [...] finally PluginLib .Free; end; end; Schon jetzt können die Packages der Clients eingebunden werden. Beispiel:
Delphi-Quellcode:
Dabei wird eine Klasse erzeugt, die das Package aus der DLL wrappt.
[...]
try PluginLib.AddPackage(TDLLLibPlgPackage.Create('projClient.dll', PluginLib)); finally [...] 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:
Um jetzt das Interface aus diesem DemoObj zu erhalten muss erstmal eine weitere Variable angelegt werden.
var
DemoObj : TIntf; [...] DemoObj := TIntf.ApplyPIntf(PluginLib.PlgClasses['demo', 'demo1'].CreatePlugin([])); [...] Beispiel:
Delphi-Quellcode:
Über DemoObj kann diese Variable befüllt werden:
var
PIDemoIntf: ^IDemo; //Pointer auf das Interface IDemo [...]
Delphi-Quellcode:
Jetzt kann die Funktion WriteSth aufgerufen werden:
[...]
DemoObj.GetPIntf(IDemo, PIDemoIntf); [...]
Delphi-Quellcode:
Und zum Schluss darf nicht vergessen werden, das Demo-Plugin wieder freizugeben:
[...]
PIDemoIntf.WriteSth; [...]
Delphi-Quellcode:
Fertig! Das erste Plugin-System wurde benutzt und kann jetzt auch mit viel komplexeren Plugins bestückt werden! (siehe Demos)
[...]
DemoObj.Free; [...] 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. |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |