Autor Beitrag
glotzer
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 393
Erhaltene Danke: 49

Win 7
Lazarus
BeitragVerfasst: Mo 23.08.10 14:50 
Plugin-System [1.0]
Das System basiert auf pointern und DLLs.
Es wurde haupsächlich auf Geschwindigkeit und möglichst geringe Datengröße optiemirt(das gesamte System ohne plugins hat eine Größe von 45kb). Es besteht aus der Plugin_System.DLL, die von dem Program zur verwaltung der Plugins verwendet wird. Jedes Plugin wird in einer DLL gespeichert, es ist möglich mehrere Plugins in einer DLL zu speichern. Auserdem wird der Unti Plugin_Types.pas benötigt, er kapselt die Basisklassen, typen, proceduren, funktionen und kümmert sich um das laden der DLL.

Das System ist nur unter windows funktionsfähig, da linux keine DLLs unterstützt.

Beispiel zur Verwendung im Programm
Initialisierung:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
var
  PSystem : TCustomPlugin_System;  //Die basis klasse aus dem unit Plugin_Types.pas
begin
  PSystem := CreatePluginSystem;   //Pluginsystem wird in DLL initialisiert 
end;


Plugins laden:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
var 
  i: Integer;
begin
  i := Psystem.LoadPluginDll(PChar('Dll-Name.DLL'));  //In i Steht die anzahl der Geladenen Plugins aus dieser DLL.
end;


funktion aufrufen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var 
  tmp: TCustomPlugin;
begin
  tmp := PSystem.GetPluginByGUID(GUID);                   //Pluginreferenz laden, am besten über GUID, aber auch über name möglich
  tmp.tmp.CallFunction('test', Parameter{pointer});       //Funktion "test" aufrufen, groß/kleinschreibung beachten, parameter und result sind vom Typ pointer
end;



Beispiel für die Programierung eines Plugins

ausblenden volle Höhe Delphi-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:
library Test_Plugin;  

uses
  SysUtils,
  Forms,
  StdCtrls,
  Plugin_Types;

type
  TMyPlugin = class(TPlugin)  //entweder von TPlugin oder von TCustom_Plugin ableiten.  TPlugin implentiert einige functionen schon, bei TCustom_Plugin sind sie abstract und müssen selbst defieniert werden 
  public
    constructor Create;
    function DoSillyThings(Parameter:pointer): pointer;
end;




var
 my: TMyPlugin;
  
procedure InitPlugins(PluginSystem: TCustomPlugin_System); cdecl;  //MUSS(!) unbedingt implentiert und exportiert werden
begin
  my := TMyPlugin.Create;
  Pluginsystem.AddPlugin(my);
  randomize
end;

constructor TMyPlugin.Create;
begin
  self.Version := 0.0;        //Plugin Version
  self.Name    := 't';        //Plugin Name (wird intern NICHT verwendet 
  self.GUID    := StringToGUID('{DE7DCBF7-175A-475C-91AD-D81C67E91746}');  //am besten hierrüber identifiezieren
  self.AddFunction('test', DoSillyThings);  //funktion hinzufügen
end;

function TMyPlugin.DoSillyThings(Parameter:pointer): pointer;
begin
  TForm(Parameter).Color := random(999999);
end;

exports
  InitPlugins name'InitPlugins';
end.
Einloggen, um Attachments anzusehen!
FinnO
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1325
Erhaltene Danke: 117

Mac OSX
TypeScript (Webstorm), Kotlin, Clojure (IDEA)
BeitragVerfasst: Mo 23.08.10 15:57 
Ich habe das jetzt nicht testen können, aber du könntest dir, um anregungen zu holfen, mal das Modular Application Framework von Helge Lange anschauen, welches sehr, sehr gut ist ;-).

Zumal seine Lösung natürlich nur bei nichtkommerziellem Einsatz kostenlos ist.
HelgeLange
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 735
Erhaltene Danke: 6

Windows 7
Delphi7 - Delphi XE
BeitragVerfasst: Mo 23.08.10 18:03 
2 kleine Anmerkungen zum Plugin-System :

1.) Deine DLL ist in einer bestimmten Delphi-Version gemacht. Das kann sehr schnell zu Problemen führen, wenn man eine andere Delphi Version nutzt. Du solltest den Code der DLL mit veröffentlichen oder die DLL in allen Delphi-Versionen zur Verfügung stellen. Beim ausführlichen Arbeiten mit DLL in Delphi wirst du Schnell feststellen, dass Du sowieso mit Runtime-Packages kompilieren musst, damit Du nur eine RTL/VCL laufen hast. Spätestens dann muss die DLL in der gleichen Delphi-Version sein.

2.) Du nutzt Namen, um Funktionen zu identifizieren. Für kleine Systeme mag das ganz ok sein, wenn es nicht zeitkritisch ist. Bei grösseren Systemen (mit ein paar tausend Funktionen) kann es schon langsam werden. Bei zeitkritischen Systemen (zum Bsp. Spielen) ist es tödlich. Besser über Nummern und Konstanten (zumindest intern).

_________________
"Ich bin bekannt für meine Ironie. Aber auf den Gedanken, im Hafen von New York eine Freiheitsstatue zu errichten, wäre selbst ich nicht gekommen." - George Bernhard Shaw
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Mo 23.08.10 18:28 
Versteh mich nicht falsch, aber für so "simple" Funktionen ist es nicht wesentlich einfacher, sicherer und klarer, Interfaces zu schreiben und diese durch exportierte Funktionen zurückgeben zu lassen?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type
  IFoo = interface
  ['{4C1DD91B-AA83-43AE-9064-9181B156EFF2}']
    procedure DoSomething;
    function ReturnString: WideString; // <- nicht "string" verwenden, da das über den Delphi MM geht. 
  end;

function CreateFoo: IFoo;

exports
  CreateFoo;

Importieren geht praktisch genau so einfach.
ausblenden Delphi-Quelltext
1:
function CreateFoo: IFoo; external 'Foo.dll';					


Das hier ist übrigens ein extrem Hack:
user profile iconHelgeLange hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
function TMyPlugin.DoSillyThings(Parameter:pointer): pointer;
begin
  TForm(Parameter).Color := random(999999);
end;


Das Argument Parameter mag zwar für die Basisapplikation ein TForm sein, aber nicht das gleiche TForm wie in der DLL. Du wendest hier praktisch die Daten aus der Basisapplikation auf die Implementation des Plugins an (!). Der Ausdruck TObject(Parameter) is TObject würde false zurückgeben.

So geht das natürlich nicht ;)
glotzer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 393
Erhaltene Danke: 49

Win 7
Lazarus
BeitragVerfasst: Mo 23.08.10 19:13 
user profile iconHelgeLange hat folgendes geschrieben Zum zitierten Posting springen:
2 kleine Anmerkungen zum Plugin-System :

1.) Deine DLL ist in einer bestimmten Delphi-Version gemacht. Das kann sehr schnell zu Problemen führen, wenn man eine andere Delphi Version nutzt. Du solltest den Code der DLL mit veröffentlichen oder die DLL in allen Delphi-Versionen zur Verfügung stellen. Beim ausführlichen Arbeiten mit DLL in Delphi wirst du Schnell feststellen, dass Du sowieso mit Runtime-Packages kompilieren musst, damit Du nur eine RTL/VCL laufen hast. Spätestens dann muss die DLL in der gleichen Delphi-Version sein.

Code liegt doch bei, was meinst du damit?

user profile iconHelgeLange hat folgendes geschrieben Zum zitierten Posting springen:

2.) Du nutzt Namen, um Funktionen zu identifizieren. Für kleine Systeme mag das ganz ok sein, wenn es nicht zeitkritisch ist. Bei grösseren Systemen (mit ein paar tausend Funktionen) kann es schon langsam werden. Bei zeitkritischen Systemen (zum Bsp. Spielen) ist es tödlich. Besser über Nummern und Konstanten (zumindest intern).

hmm gute idee, werd ich in der nächsten version machen


user profile icondelfiphan hat folgendes geschrieben Zum zitierten Posting springen:
Versteh mich nicht falsch, aber für so "simple" Funktionen ist es nicht wesentlich einfacher, sicherer und klarer, Interfaces zu schreiben und diese durch exportierte Funktionen zurückgeben zu lassen?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type
  IFoo = interface
  ['{4C1DD91B-AA83-43AE-9064-9181B156EFF2}']
    procedure DoSomething;
    function ReturnString: WideString; // <- nicht "string" verwenden, da das über den Delphi MM geht. 
  end;

function CreateFoo: IFoo;

exports
  CreateFoo;

Importieren geht praktisch genau so einfach.
ausblenden Delphi-Quelltext
1:
function CreateFoo: IFoo; external 'Foo.dll';					



könnte gut sein aber ich hab noch nie mit interfacen gearbeited, deshalb hab ich die version über pointer verwendet.
Eventuell kümmer ich mich in späteren Versionen noch darum.

user profile icondelfiphan hat folgendes geschrieben Zum zitierten Posting springen:

Das hier ist übrigens ein extrem Hack:
user profile iconHelgeLange hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
function TMyPlugin.DoSillyThings(Parameter:pointer): pointer;
begin
  TForm(Parameter).Color := random(999999);
end;


Das Argument Parameter mag zwar für die Basisapplikation ein TForm sein, aber nicht das gleiche TForm wie in der DLL. Du wendest hier praktisch die Daten aus der Basisapplikation auf die Implementation des Plugins an (!). Der Ausdruck TObject(Parameter) is TObject würde false zurückgeben.

So geht das natürlich nicht ;)

funktioniert doch 8)
naja, soll ja nur ein beispiel sein und das schreibt sich dann noch am schnellsten