Entwickler-Ecke

Off Topic - Programm mit Plugins...


Anonymous - Mo 06.10.03 20:31
Titel: Programm mit Plugins...
Hallo,


ich habe vor ein Programm zu schreiben indem ich Plugins einbinden kann, weiß aber leider noch nciht genau wie ich vorgehen muss...

Ich möchte die Plugins als Dlls einbinden, doch ich habe nie zuvor mit dlls gearbeitet. Weiß da jemand Tut's?

Sind dll's immer direkt mit nonvcl Programmierung verbunden?

Vile Fragen, ich hoffe mir kann jemand Helfen...


BungeeBug - Mo 06.10.03 20:58

Hi,

dll proggen is genau so wie nen normales Progamm auch. Kannst also beides machen, jenachdem was die besser passt :)

Und zu den Tuts, guck mal auf http://www.delphi-treff.de oder bei den andern Tutorials


CodeWicht - Mo 06.10.03 21:11

ein gutes tut findest du unter:

DLL-Tut [http://www.tutorials.delphi-source.de/dlls/]

Du musst im vornherein wissen, was dein programm, bzw. deine zukünftigen plugins leisten sollen. demnach musst du entscheiden, ob du die statische einbindung, die dynamische oder die dynamisch-dynamische(Funktionen aus DLLs benutzen, die bei der Kompilierung des Programms noch gar nicht bekannt sind) benutzen willst, oder alle ;)

In Dlls kann man eigentlich alles reinpacken, was geht, Formulare, Ressourcen, nonVCL etc.

Ich selbst habe ein Plugin-System auf Basis von DLLs geschrieben (siehe TitanBox im Freeware-Teil). Bei Problemen kann ich dir gerne weiterhelfen.

Grüsse, CodeWicht.


MSCH - Mo 06.10.03 21:41

Für PlugIns kommt statische Bindung nicht in Frage, da zur Laufzeit die DLL vorhanden sein muss! Also Dynamische Bindung - ist für Plugins sowieso besser.

grez
msch


Anonymous - Mo 06.10.03 21:43

Ok, aber wie kann ich auf die Formulare und Resourcen einer dll zugreifen?

Das normale Prinzip der functionen habe ich verstanden....


hmm


Anonymous - Mo 06.10.03 22:03

du kannst in einer Dll ein Formular anzeigen lassen, falls du das meinst.
Für Ressourcen, guck dir mal LoadResource an.


sakura - Di 07.10.03 10:11

Dann möchte ich mal auf mein Tutorial hinweisen ;-) Der zweite Teil behandelt auch PlugIns welche ein Form zur Einrichtung von ShortCuts bieten welche wiederum die Hauptanwendung manipulieren - ergo - eigentlich die gesamte Palette der Grundbedürfnisse abdecken.

PlugIns in eigenen Anwendungen [http://www.delphipraxis.net/topic5390_plugins+in+eigenen+anwendungen.html]

:welcome:


Anonymous - Di 07.10.03 18:02

Danke für die ganzen Tipps!


THX


CenBells - Di 07.10.03 19:38

@Sakura, machst du noch ein tutorial über plugins mit interfaces?
Und was mich ganz doll interessiert (weil ich es b isher nicht hinbekommen habe) sind frames in einem plugin unterzubringen.

Gruß
Ken


sakura - Di 07.10.03 22:07

Frames habe ich noch nicht in PlugIns versucht *g* Aber das liegt daran, daß ich die Frame-Technologie nicht sonderlich mag. An den Interfaces werde ich mich bestimmt mal ranmachen - nutzt unsere Software ja auch.

8)


CenBells - Di 07.10.03 22:16

das hört sich gut an.
Wenn ich dich richtig verstehe sind deine plugins doch dlls, oder?

Gruß
Ken


maximus - Di 07.10.03 22:31

Ich würde mich für BPL plugIns entscheiden, weil das der 'native' weg ist delphi code einzubinden...damit kann man sollte man locker frames einbinden können.

kannst ja mal diesen artikel von Borland lesen:

http://community.borland.com/article/0,1410,27178,00.html


sakura - Di 07.10.03 22:45

BPLs sind für Frames sicher die beste Lösung, allerdings muss man auch die Nachteile sehen. BPLs sollte man generell nur mit der gleichen Delphi-Compiler-Version erstellen (auch Updates des Compilers). Das bringt natürlich Nachteile, wenn man die Schnittstellen auch Programmierern anderer Firmen zur Verfügung stellen will - wären aber die logische Weiterführung zu meinem Tutorial ;-)

Wie dem auch sei, werde ich die Interfaces als nächstes in Angriff nehmen, da man damit halt auch MS C++ und VB Entwicklern die Möglichkeit gibt auf die PlugIns-Schnittstellen korrekt zu reagieren :-)

8)


CenBells - Di 07.10.03 23:26

danke, das mit den bpls habe ich schon oft versucht, aber möchte mich eher dagegen entscheiden. Ich spiele nach einiger zeit der Pause mal wieder mit frames und dlls rum.
Jetzt habe ich etwas ganz merkwürdiges. wenn ich einen string an die dll übergebe bekomme ich immer fehlermeldungen. (Sharemem ist eingebunden)

ich habe ein interface definiert

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  IPluginFrame = interface
  ['{5A3D518B-76B8-4867-B7AC-3233AE9DF2FD}']
    function igettext: String;
    procedure IcbsetParent(AParent: THandle);
    function igetself:TFrame;
    procedure isettext(Atext: String);
  end;


und die implementierung im frame sieht so aus


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TFrame1.isettext(Atext: String);
begin
  application.MessageBox(PChar(AText), 'hilfe');
  //edit1.Text := AText;
end;


den zugriff auf das frame in der dll mache ich so


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:
45:
46:
47:
//subunit code
function getPluginFrame(AApplication: TApplication): TFrame;

implementation

uses SysUtils;

var
  vFrame: TFrame1;

function getPluginFrame(AApplication: TApplication): TFrame;
begin
  Application := AApplication;
  if not assigned(vFrame) then
    vFrame := TFrame1.Create(Application);
  result := vFrame;
end;

//dll code

library pluginFrame;

{ Wichtiger Hinweis zur DLL-Speicherverwaltung: ShareMem muss sich in der
  ersten Unit der unit-Klausel der Bibliothek und des Projekts befinden (Projekt-
  Quelltext anzeigen), falls die DLL Prozeduren oder Funktionen exportiert, die
  Strings als Parameter oder Funktionsergebnisse übergeben. Das gilt für alle
  Strings, die von oder an die DLL übergeben werden -- sogar für diejenigen, die
  sich in Records und Klassen befinden. Sharemem ist die Schnittstellen-Unit zur
  Verwaltungs-DLL für gemeinsame Speicherzugriffe, BORLNDMM.DLL.
  Um die Verwendung von BORLNDMM.DLL zu vermeiden, können Sie String-
  Informationen als PChar- oder ShortString-Parameter übergeben. }

  

uses
  Sharemem,
  SysUtils,
  Classes,
  frameplugintest in 'frameplugintest.pas' {Frame1: TFrame},
  dllexport in 'units\dllexport.pas';

{$R *.res}

exports
  getPluginFrame;

begin
end.


in meiner mainexe sieht der code so aus


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function getPluginFrame(AApplication: TApplication): TFrame; external 'pluginFrame.dll';

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  LIntf: IPluginFrame;
begin
  FPluginFrame := getPluginFrame(Application);
  Panel1.Caption := FPluginFrame.ClassName;
  FPluginFrame.Parent := Panel2;
  if supports(FPluginFrame, IPluginFrame, LIntf) then
    LIntf.isettext('HALLO WELTENBUMMLER EREWRW RWER WERWERW RWE RWE R WER');
end;


Das auslesen des classnamen klappt, das interface bekomme ich auch, aber dann hört der spaß auf.

Woran könnte das liegen?

Gruß
Ken


maximus - Mi 08.10.03 10:34

sakura hat folgendes geschrieben:
BPLs sind für Frames sicher die beste Lösung, allerdings muss man auch die Nachteile sehen. BPLs sollte man generell nur mit der gleichen Delphi-Compiler-Version erstellen (auch Updates des Compilers).
...


:? Wusste ich garnicht...kann man das nicht irgendwie standardisieren oder umgehen? ...das wäre ja ein gravierender nachteil :idea: ..doch was ist mit bisschen älteren componenten, die können ja auch in BPLs sein...die müssen dann doch noch funktionieren?


sakura - Mi 08.10.03 13:29

maximus hat folgendes geschrieben:
doch was ist mit bisschen älteren componenten, die können ja auch in BPLs sein...die müssen dann doch noch funktionieren?

Auch (Gerade) für Komponenten benötigst Du immer die BPLs, welche mit der entsprechenden Compilerversion erstellt wurden :?

:wink2:


maximus - Mi 08.10.03 15:14

:? Scheise *g*

...hab mit DLLs noch nicht so viel gemacht: Ist es möglich normale klassen (oder VCL klassen) in DLLs zu packen, diese dann zu registrieren, und normal zu benutzen? ...und wie muss die VCL dann gebunden werden (sie darf ja nur einmal im projekt vorkommen und die DLL muss sie auch kennen)? ...vielleicht via BPL, wobei wir wieder ein problem hätten ???


sakura - Mi 08.10.03 15:34

Teilweise, deswegen hatte ich in meinem Tutorial auch geschrieben, daß die Technik mit Vorsicht zu genießen ist. Borland behält sich das Recht vor, die Basisklassen deren Bedürfnissen von Version zu Version anzupassen. Sonst wären Erweiterungen wie z.B. Anchors, Constraints und änhliches nie möglich gewesen.

Wenn man eigene Klassen erstellt, welche direkt von TObject abgeleitet sind, dann ist man relativ sicher, daß auch andere Delphi-Compiler Versionen mithalten können, da sich diese Klasse nicht so oft ändert.

Delphi 3 Version von TObject

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:
  TObject = class
    constructor Create;
    procedure Free;
    class function InitInstance(Instance: Pointer): TObject;
    procedure CleanupInstance;
    function ClassType: TClass;
    class function ClassName: ShortString;
    class function ClassNameIs(const Name: string): Boolean;
    class function ClassParent: TClass;
    class function ClassInfo: Pointer;
    class function InstanceSize: Longint;
    class function InheritsFrom(AClass: TClass): Boolean;
    procedure Dispatch(var Message);
    class function MethodAddress(const Name: ShortString): Pointer;
    class function MethodName(Address: Pointer): ShortString;
    function FieldAddress(const Name: ShortString): Pointer;
    function GetInterface(const IID: TGUID; out Obj): Boolean;
    class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
    class function GetInterfaceTable: PInterfaceTable;
    function SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): Integer; virtual;
    procedure DefaultHandler(var Message); virtual;
    class function NewInstance: TObject; virtual;
    procedure FreeInstance; virtual;
    destructor Destroy; virtual;
  end;


Delphi 7 Version von TObject

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:
  TObject = class
    constructor Create;
    procedure Free;
    class function InitInstance(Instance: Pointer): TObject;
    procedure CleanupInstance;
    function ClassType: TClass;
    class function ClassName: ShortString;
    class function ClassNameIs(const Name: string): Boolean;
    class function ClassParent: TClass;
    class function ClassInfo: Pointer;
    class function InstanceSize: Longint;
    class function InheritsFrom(AClass: TClass): Boolean;
    class function MethodAddress(const Name: ShortString): Pointer;
    class function MethodName(Address: Pointer): ShortString;
    function FieldAddress(const Name: ShortString): Pointer;
    function GetInterface(const IID: TGUID; out Obj): Boolean;
    class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
    class function GetInterfaceTable: PInterfaceTable;
    function SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): HResult; virtual;
    procedure AfterConstruction; virtual;
    procedure BeforeDestruction; virtual;
    procedure Dispatch(var Message); virtual;
    procedure DefaultHandler(var Message); virtual;
    class function NewInstance: TObject; virtual;
    procedure FreeInstance; virtual;
    destructor Destroy; virtual;
  end;


Beide Versionen führen keine neuen Felder ein und sind soweit schon mal zueinander kompatibel, allerdings wurden in Delphi 4 oder 5 :?!?: einige Änderungen eingebracht.


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
 // V3
    procedure Dispatch(var Message);
 // V7
    procedure Dispatch(var Message); virtual;


 // V3
    function SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): Integer; virtual;
 // V7
    function SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): HResult; virtual;


 // V3
 // gabs nicht ;-)
 // V7
    procedure AfterConstruction; virtual;
    procedure BeforeDestruction; virtual;


Dadurch kann es zu Konflikten kommen, die man vorher eingehendst studieren sollte und ggf. den Gebrauch jeglicher Basismethoden durch den Programmierer ausschließen. An dieser Stelle verweise ich mal auf folgendes Tutorial Ändern der Klassenhierarchie [http://www.delphipraxis.net/topic10605_aendern+der+klassenhierarchie.html], die dort gezeigten Techniken dürfen zum Beispiel auch nicht genutzt werden, wenn man Klassennutzung über DLLs zulässt, da die verschiedenen Delphi-Version unterschiedlich VMT's nutzen.

Das ist auch der Grund, warum ich mal endlich den nächsten Teil des PlugIn Tuts machen sollte, damit man sich den Interfaces (ab IDispatch) annehmen kann, die viele der hier aufgeführten Probleme lösen :-)

:wink2:


maximus - Mi 08.10.03 21:41

:D Danke für diese weisen worte *g* ...du scheinst es wirklich präzise zu wissen...schöne antwort das.

Das klassen-injezierungs-gelöt gefällt mir auch sehr gut! ...was ist eigentlich, wenn ich features einer, injezierten klasse, schon im code nutzen will? ...*überlegt*...würde aber auch keinen sinn machen und der compiler weiss ja noch nix von seinem glück *g*

Kann ich mit der technik auch eine klasse vor TObject schalten?


Mit den plugIns überleg ich noch, welche technik ich einsetzen werde...

mfg


sakura - Do 09.10.03 10:54

maximus hat folgendes geschrieben:
Kann ich mit der technik auch eine klasse vor TObject schalten?

Es ist prinzipiell möglich, wird jedoch nicht viel bringen da

Die Methoden, wie im Tutorial vorgestellt unterbinden die Möglichkeit durch folgende Zeile:

Delphi-Quelltext
1:
2:
    if clSuccessor.ClassParent = nil then 
      raise Exception.Create('Successor Class has no parent to be replaced.');

Da wird abgefangen, da die zu ändernde Klasse einen Vorgänger hat. Änderst Du diesen bei TObject musst Du darauf achten, daß der neue Vorgänger keinen Vorgänger hat - er muss auf nil gesetzt werden, sonst gibt es garantiert einige Probleme ;-)

:wink2:


maximus - Do 09.10.03 11:37

Aha. schön das wir mal drüber geredet haben :) ...ich bin immer sehr dafür meinen Delphi-horizont zu erweitern und technicken zu lernen die über die offensichtlichen features hinnaus und tiefer gehen :wink: