Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Tutorials und Kurse (https://www.delphipraxis.net/36-tutorials-und-kurse/)
-   -   Delphi PlugIns in eigenen Anwendungen (https://www.delphipraxis.net/4851-plugins-eigenen-anwendungen.html)

sakura 14. Mai 2003 13:25


PlugIns in eigenen Anwendungen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Nachdem ich gezeigt habe, wie man Erweiterungen (PlugIns) für die Delphi-IDE erstellen kann, möchte ich dieses Mal zeigen, wie man dieses Konzept in eigenen Anwendungen nutzen kann. Dazu werde ich das Tutorial in mehrere Abschnitte aufteilen.

Der erste Abschnitt zeigt die denkbar einfachste Form ein PlugIn zu erstellen. Diese Methode ist nicht unbedingt sehr flexibel, erlaubt es jedoch, schnell einfache Erweiterungen zu erstellen. Vorteil ist, daß das Thema auch für "Neulinge" auf diesem Bereich recht einfach betrachtet werden kann.

Um dieses zu erreichen, werde ich die erste Variante als "Delphi-Only-Lösung" vorstellen, daß heißt, daß auch die PlugIns mit Delphi erstellt werden müssen.

Code:
[color=#000080][i]HINWEIS:[/i]
Diese Variante benötigt die Unit ShareMem, welche wiederum voraussetzt, daß die Library [i]BORLNDMM.DLL[/i]
zusammen mit der Anwendung ausgeliefert wid. Diese DLL befindet sich im Delphi\Bin Verzeichnis und darf
zusammen mit der Anwendung weitergegeben werden. In älteren Delphi-Versionen (3...) ist es möglich, daß die
Borland DLL einen anderen Namen hat.

Die Unit ShareMem muß als erstes in der Uses-Klausel des Projektes und aller PlugIns stehen![/color]
PlugIns, wie kommen die eigentlich in die Anwendung

Es gibt verschiedene Möglichkeiten, woran eine Anwendung erkennt, das diese PlugIns zu laden hat. Alle Möglichkeiten setzen eines jedoch voraus: Die Anwendung muß so konzipiert sein, daß diese PlugIns überhaupt unterstützt.

In diesem Beispiel sucht die Anwendung nach der Datei PlugIns.ini, welche im gleichen Verzeichnis wie die Anwendung liegen muß. Die Ini-Datei sieht wie folgt aus.
Code:
[info]
info1=This file contains a list of all plugins.
info2=each plugin has its own section
info3=each plugin section has to contain a string "type=plugin"
info4=each plugin section has to contain a string "dllname" pointing to the plugin file

[sample01]
type=Plugin[color=#800000](off)[/color]
dllname=plugin01.dll

[sample02]
type=Plugin[color=#800000](off)[/color]
dllname=plugin02.dll
Die rot dargestellten Bereiche müssen gelöscht werden, damit die PlugIns geladen werden. In den ZIP-Files sind die Plug-Ins so eingestellt, daß diese (per Default) erst einmal nicht geladen werden, damit die Anwendung ohne Plug-Ins getestet werden kann. Wenn die Strings (off) gelöscht sind, werden die Plug-Ins beim nächsten Start automatisch geladen, dann kann das "neue" Verhalten der Anwendung getestet werden.

Andere Möglichkeiten nach Plug-Ins zu suchen

Oft werden zwei weitere Methoden genutzt, um Plug-Ins zu suchen.
  • Die Plug-Ins müssen in der Registry unter einem bestimmten Schlüssel eingetragen werden (z.B. Borland Delphi)
  • Die Plug-Ins müssen in einem bestimmten Verzeichnis liegen.
Letztere Methode ist nur bedingt empfehlenswert, da diese voraussetzt, das in dem Verzeichnis ausschließlich Plug-Ins liegen, andere Dateien können zu Problemen führen.

Plug-Ins zulassen

In diesem Beispiel werden wir ausschließlich Plug-Ins zulassen, welche als DLLs geladen werden können. Diese Plug-Ins müssen eine Funktion mit dem Namen LoadPlugIn exportieren, welche folgende Paramter hat:
Delphi-Quellcode:
function LoadPlugin(Parent: THandle; var PlugIn: TPlugIn): Boolean; export;
Der erste Parameter Parent erhält den Handle der Anwendung, welche das Plug-In lädt. Der zweite Paramter PlugIn kann vom Plug-In genutzt werden, um ein Object zurückzuliefern. Wird keines zurückgeliefert, so wird da Plug-In nicht im Menü angezeigt.

Die Applikation lädt die Plug-Ins mit Hilfe der Win-API Funtion LoadLibrary. LoadLibrary liefert einen Handle auf die geladene Library zurück. Wenn die Library erfolgreich geladen wurde, versucht Applikation die Methode LoadPlugIn mit Hilfe der Win-API Funtion GetProcAddress zu laden.
Code:
[color=#000080][i]HINWEIS:[/i]
Bei dem Export von Funktionen aus DLLs muß Groß- und Kleinschreibung beachtet werden (C++-Standard)!!![/color]
Nach erfolgreichem Ladevorgang der Prozedur wird diese aufgerufen und das Plug-In initialisiert.
Code:
[color=#000080][i]HINWEIS:[/i]
In diesem Beispiel muß sich das PlugIn um die Initialisierung des zurückgelieferten Objektes kümmern.
Die Applikation übernimmt die Zerstörung des Objektes, wenn dieses nicht mehr gebraucht wird.[/color]
Das exportierte Plug-In Objekt

Damit die Applikation nach Bedarf auf das Plug-In zurgreifen kann, muß dieses ein Objekt zurückliefern, welches durch die Applikation erkannt wird. Damit sowohl die Applikation, als auch jedes Plug-In vom gleichen Objekttyp "sprechen", wird dieser in einer gemeinsamen Unit definiert. Diese Unit ist dem Download beigefügt (\Shared Units\uPlugInDefinition.pas).
Delphi-Quellcode:
//Auszug aus der Unit
type
  TPlugIn = class
  public
    constructor Create(aParent: THandle);
    // information strings
    function GetName: string; virtual; stdcall; abstract;
    function GetAuthor: string; virtual; stdcall; abstract;
    function GetComment: string; virtual; stdcall; abstract;
    // menu data
    function GetMenuText: string; virtual; stdcall; abstract;
    function GetEnabled: Boolean; virtual; stdcall; abstract;
    // launch the Expert
    procedure Execute; virtual; stdcall; abstract;
    // event handler
    function EventHandler(EventNotification: TEventNotification): TEventAction;
        virtual; stdcall; abstract;
  end;

  TLoadPlugIn = function(Parent: THandle; var PlugIn: TPlugIn): Boolean;
Einerseits ist hier die abstrakte Definition, welche daß durch jedes Plug-In zu exportierende Objekt zeigt. Dieses Objekt liefert verschiedene Informationen zurück. Unter anderem wird gesteuert, wie sich das Plug-In im Menü darstellt und auf welche Ereigenisse reagiert wird. Desweiteren ist auch das Format der zu exportierenden Funktion TLoadPlugIn definiert.

Das Plug-In I

Jedes Plug-In, welches interaktiv angesteuert werden kann, muss ein Objekt vom Typ TPlugIn exportieren. Dieses Objekt muß alle abstrakten Methoden implementieren, welche durch die abstrakte Klasse TPlugIn definiert sind.

Im ersten Plug-In sieht die Klassendefinition wie folgt aus
Delphi-Quellcode:
type
  TPlugIn01 = class(TPlugIn)
  public
    // information strings
    function GetName: string; override; stdcall;
    function GetAuthor: string; override; stdcall;
    function GetComment: string; override; stdcall;
    // menu data
    function GetMenuText: string; override; stdcall;
    function GetEnabled: Boolean; override; stdcall;
    // launch the Expert
    procedure Execute; override; stdcall;
    // event handler
    function EventHandler(EventNotification: TEventNotification): TEventAction;
        override; stdcall;
  end;

{ TPlugIn01 }

function TPlugIn01.EventHandler(EventNotification: TEventNotification):
    TEventAction;
begin
  // nothing to do
  Result := evnContinue;
end;

...
Alle Methoden werden implementiert. Das erste Plug-In führt keine weiteren Aktionen aus. Wird sein Menüpunkt durch den Nutzer aktiviert, so wird lediglich eine Messagebox angezeigt.

Das Plug-In II

Das zweite Plug-In ist ein wenig komplexer. Es reagiert auf mehrere Ereignisse (File->New, File->Open, File->Save). Dazu wird in der Methode EventHandler auf entsprechende Ereignisse reagiert.
Delphi-Quellcode:
function TPlugIn02.EventHandler(
  EventNotification: TEventNotification): TEventAction;
begin
  case EventNotification of
    evnFileNew:
      if MessageBox(Parent, 'Änderungen verwerfen?', 'Öhm', MB_ICONWARNING or
          MB_YESNO) = IDYES then
        Result := evnContinue
      else
        Result := evnAbort;
    evnFileOpen:
      if MessageBox(Parent, 'Änderungen verwerfen?', 'Öhm', MB_ICONWARNING or
          MB_YESNO) = IDYES then
        Result := evnContinue
      else
        Result := evnAbort;
    evnFileSave:
    begin
      MessageBox(Parent, 'Bitte "Speichern als..." wählen', 'Öhm',
          MB_ICONINFORMATION or MB_OK);
      Result := evnAbort;
    end;
  else
    Result := evnContinue;
  end;
end;
Damit das Plug-In auf diese Ereignisse reagieren kann, muß die Applikation das Plug-In informieren, wenn solche Ereignisse auftreten. Dazu stelle ich Ihnen die Methode RunEvent aus der Applikation vor.
Delphi-Quellcode:
function TForm1.RunEvent(EventNotification: TEventNotification): Boolean;
var
  I: Integer;
begin
  for I := 0 to High(FPlugIns) do
    if FPlugIns[I].PlugIn <> nil then
    begin
      if FPlugIns[I].PlugIn.EventHandler(EventNotification) = evnAbort then
      begin
        Result := False;
        Exit;
      end;
    end;
    Result := True;
end;
Diese Methode sendet ein Ereignis an alle Plug-Ins. Wenn alle Plug-Ins den Status evnContinue zurückliefern, dann liefert die Methode True zurück, ansonsten False. Diese Methode wird zum Beispiel aufgerufen, wenn der User im Menü File auf New klickt.
Delphi-Quellcode:
procedure TForm1.New1Click(Sender: TObject);
begin
  if RunEvent(evnFileNew) then
  begin
    mmoFileContent.Clear;
    FFileName := '';
  end;
end;
Wenn kein Plug-In geladen ist, wird der aktuelle Inhalt des Memos einfach gelöscht. Wenn das zweite Plug-In geladen ist, fragt dieses den User, ob er die Inhalte des Memos wirklich verwerfen will. Antwortet der mit "nein", liefert das Plug-In II evnAbort zurück und das Memo wird nicht gelöscht.

Der komplette Code

Der komplette Code für dieses einfache Beispiel kann von Mitgliedern der Delphi-PRAXiS frei runtergeladen werden. Wenn Fragen bestehen, einfach ins Forum posten. Mal schauen...

Der nächste Schritt

Im nächsten Schritt werde ich ein etwas komplexeres Beispiel zeigen. Voraussetzung dafür wird ein Verständnis von Delphi-Interfaces sein. Wann es kommt, steht noch nicht fest ;-)

...:cat:...

MichaelK 14. Mai 2003 15:47

Hi!

Ich finde dein Tutorial sehr gut: es ist verständlich, gut strukturiert und vorallem praktisch. Mir hat es sehr geholfen meine Andwendungen flexibel zu gestalten (hab ja mehr oder weniger um ein Tutorial "gebeten", auch wenn ich's nicht direkt gesagt hab ;-))

Ich freue mich schon auf den zweiten Teil!

sakura 15. Mai 2003 16:16

Obwohl einigen schon für heute versprochen :oops: kommt der zweite Teil erst morgen. Dafür aber ohne Interfaces ;-) aber um so cooler.

...:cat:...

sakura 16. Mai 2003 15:09

Liste der Anhänge anzeigen (Anzahl: 2)
Dieser zweite Teil stellt ein etwas komplexeres Beispiel vor. Bevor Sie sich diesen Teil durchlesen, empfehle ich dringend den ersten Teil, falls Sie sich vorher nicht mit Erweiterungen, DLLs oder ähnlichem ausführlich beschäftigt haben. Gute Kenntnisse in der objekt-orientierten Programmierung sind zwingend erfordlich, da dieses Tutorial dafür keinen Platz bietet.

In diesem Teil stelle ich Ihnen eine Methode vor, welche sehr stark an die Art angelehnt ist, wie auch Delphi diese bereits seit mehreren Versionen unterstützt. Nachteil dieser Methode ist, daß auch die Plug-Ins in Borland Delphi bzw. Borland C++ erstellt werden müssen. Der Vorteil ist die relative "Einfachheit" der Strategie.

Code:
[color=#000080][i]HINWEIS:[/i]
Auch diese Version nutzt die Unit ShareMem.
Die Unit ShareMem muß als erstes in der Uses-Klausel des Projektes und aller PlugIns stehen![/color]
Registrieren der Plug-Ins

Um auch einen anderen Weg der Registrierung von Plug-Ins zu zeigen, habe ich dieses Mal einen Eintrag in die Registry gewählt. Ähnlich dem Verfahren, welches auch Borland Delphi nutzt. Alle zu ladenen Plug-Ins müssen sich unter folgendem Registry-Key eintragen.
HKCU\Software\Delphi-PRAXiS\Extendable Application\PlugIns

Für jedes Plug-In muß ein String-Wert eingetragen werden, dessen Value den Pfad und den Namen der Plug-In-DLL enthalten. Der Name des Wertes kann frei gewählt werden.

Das Design der Software

Unter Design ist nicht das Aussehen der Anwendung zu verstehen, als viel mehr der interne Aufbau des Codes! In einer öffentlichen Unit (Shared Units\uAppIntf.pas) werden die abstrakten Basisklassen (TIApplication, TIMainMenu, TIMenuItems, TIMenuItem, TIDocuments, TIDocument, TIDocument) vorgestellt, welche durch die Anwendung den Plug-Ins zur Verfügung gestellt werden. Zusätzlich wird auch die Basisklasse (TPlugInObject) vorgestellt, welche durch jedes Plug-In exportiert werden muß.

Diese Unit muß sowohl von der Applikation als auch von jedem Plug-In importiert werden. Mit einer Ausnahme sind alle Klassenmethoden als abstrakt definiert. Die Ausnahme bildet der constructor der Klasse TPlugInObject, welcher eine Referenz auf das von der Applikation exportierte Hauptobjekt speichert.

Die Aufgaben der Applikation

Damit die Applikation dem Plug-In wirkliche Möglichkeiten zur Erweiterung bietet, muß diese Schnittstellen nach aussen geben. Die Startschnittstelle ist TIApplication und diese ist in diesem Beispiel wie folgend definiert.
Delphi-Quellcode:
  TIApplication = class
  public
    function GetHandle: Integer; virtual; stdcall; abstract;
    function GetMainMenu: TIMainMenu; virtual; stdcall; abstract;
    function GetDocuments: TIDocuments; virtual; stdcall; abstract;
    function GetActiveDocument: TIDocument; virtual; stdcall; abstract;
    procedure ShowMessage(const Msg: String); virtual; stdcall; abstract;
  end;
Während der Initialisierung des Plug-Ins erhält das Plug-In von der Anwendung eine Referenz auf ein Objekt vom Typ TIApplication. Diese Objekt bietet in diesem Beispiel fünf Methoden.
  • GetHandle liefert das Handle der Anwendung zurück
  • GetMainMenu erstellt ein Objekt vom Typ TIMainMenu und liefert dieses an die Anwendung zurück, welche dieses anschließend nutzen kann, um das Hauptmenü zu erweitern
  • GetDocuments erstellt ein Objekt vom Typ TIDocuments und gibt dem Plug-In die Möglichkeit mit den einzelnen, geöffneten Dokumenten zu arbeiten.
  • [i]GetActiveDocument[I] erstellt ein Objekt vom Typ TIDocument. Die Plug-Ins haben so jederzeit Zugriff auf das aktuelle Dokument.
  • ShowMessage fordert die Anwendung auf, dem Nutzer eine MessageBox zu zeigen

Code:
[color=#000080][i]HINWEIS:[/i]
Die Anwendung erstellt sämtliche Objekte (außer TIApplication) ausschließlich für die Plug-Ins.
Wenn die Plug-Ins das Objekt nicht mehr benötigen, dann müssen diese das Objekt auch wieder freigeben,
um Resourcen zu sparen.[/color]
Das Menü dem Client anbieten

Der Trick. Wie schon mit der Applikation, möchten wir dem Plug-In keinen direkten Zugriff auf das Menü gewähren, um den dadurch entstehenden Risiken vorzubeugen. Das bringt natürlich auch etwas Arbeit mit sich, da ein Wrapper für das Menü und sämtliche Menüpunkte erstellt werden muss.

Dazu werden die folgenden abstrakten Klassen definiert, welche auch den Plug-Ins bekannt sind:
Delphi-Quellcode:
  TIMainMenu = class
  public
    function GetMenuItems: TIMenuItems; virtual; stdcall; abstract;
    function InsertMenuItem(aIndex: Integer; aCaption, aHint, aName: String;
        aShortCut: Word; aTag: Integer; OnClick: TOnMenuClick): TIMenuItem;
        virtual; stdcall; abstract;
  end;

  TIMenuItems = class
  public
    function GetCount: Integer; virtual; stdcall; abstract;
    function GetItem(Index: Integer): TIMenuItem; virtual; stdcall; abstract;
    function FindItem(aName: String): TIMenuItem; virtual; stdcall; abstract;
    function RemoveItem(aMenuItem: TIMenuItem): Boolean; virtual; stdcall;
        abstract;
  end;

  TIMenuItem = class
  public
    function GetMenuItems: TIMenuItems; virtual; stdcall; abstract;
    function InsertMenuItem(aIndex: Integer; aCaption, aHint, aName: String;
        aShortCut: Word; aTag: Integer; OnClick: TOnMenuClick): TIMenuItem;
        virtual; stdcall; abstract;
    function FindItem(aName: String): TIMenuItem; virtual; stdcall; abstract;

    function GetIndex: Integer; virtual; stdcall; abstract;
    function GetCaption: String; virtual; stdcall; abstract;
    procedure SetCaption(aValue: String); virtual; stdcall; abstract;
    function GetHint: String; virtual; stdcall; abstract;
    procedure SetHint(aValue: String); virtual; stdcall; abstract;
    function GetName: String; virtual; stdcall; abstract;
    function GetShortCut: Word; virtual; stdcall; abstract;
    procedure SetShortCut(aValue: Word); virtual; stdcall; abstract;
    function GetTag: Integer; virtual; stdcall; abstract;
    procedure SetTag(aValue: Integer); virtual; stdcall; abstract;
    function GetOnClick: TOnMenuClick; virtual; stdcall; abstract;
    procedure SetOnClick(aValue: TOnMenuClick); virtual; stdcall; abstract;
  end;
TIMainMenu wird vom Applikations-Objekt zurückgeliefert. Von dort kann dann das Plug-In auf alle Menüpunkte zugreifen, eigene Menüpunkte und ganze Menüs einbauen, etc.

In der Applikation

Die Applikation bietet Klassen, welche die einzelnen Menüpunkte verwalten und die gewünschten Schnittstellen nach außen bieten.
Delphi-Quellcode:
  TMainMenuImpl = class(TIMainMenu)
  private
    FMenuItem: TMenuItem;
    FAppIntf: TApplicationImpl;
  public
    constructor Create(aAppIntf: TApplicationImpl; aMenuItem: TMenuItem);
    // overrides
    function GetMenuItems: TIMenuItems; override; stdcall;
    function InsertMenuItem(aIndex: Integer; aCaption, aHint, aName: String;
        aShortCut: Word; aTag: Integer; OnClick: TOnMenuClick): TIMenuItem;
        override; stdcall;
  end;

...

constructor TMainMenuImpl.Create(aAppIntf: TApplicationImpl; aMenuItem:
    TMenuItem);
begin
  inherited Create;
  FAppIntf := aAppIntf;
  FMenuItem := aMenuItem;
end;

function TMainMenuImpl.GetMenuItems: TIMenuItems;
begin
  Result := TMenuItemsImpl.Create(FAppIntf, FMenuItem);
end;

function TMainMenuImpl.InsertMenuItem(aIndex: Integer; aCaption, aHint,
  aName: String; aShortCut: Word; aTag: Integer;
  OnClick: TOnMenuClick): TIMenuItem;
var
  MI: TMenuItem;
  PMI: TPluginMenuItem;
begin
  MI := TMenuItem.Create(frmMain);
  MI.Caption := aCaption;
  MI.Hint := aHint;
  MI.Name := aName;
  MI.ShortCut := aShortCut;
  MI.Tag := aTag;
  MI.OnClick := nil;
  FMenuItem.Insert(aIndex, MI);

  PMI := TPluginMenuItem.Create(FAppIntf, MI);
  PMI.OnMenuClick := OnClick;
  FAppIntf.FPlugInMenus.Add(PMI);

  Result := TMenuItemImpl.Create(FAppIntf, MI);
end;
Dazu speichert sich der Menü-Wrapper eine Referenz auf das Applikationsobjekt (um diese an die einzelnen Wrapper der Menüpunkt durchzureichen) und eine Referenz auf das Menüitem, welches die Referenz auf den zu bearbeitenden Menüpunkt hält. Desweiteren fallen die folgenden Zeilen auf
Delphi-Quellcode:
  PMI := TPluginMenuItem.Create(FAppIntf, MI);
  PMI.OnMenuClick := OnClick;
  FAppIntf.FPlugInMenus.Add(PMI);
Da der Klick auf ein dynamisches Menüitem nicht automatisch durchgereicht werden soll (Sicherheitsaspekte) wird der Klick auf ein separat erstelltes Objekt gelenkt. Dieses Objekt stellt die interne Klick-Methode zur Verfügung
Delphi-Quellcode:
procedure TPluginMenuItem.OnClick(Sender: TObject);
var
  TMII: TMenuItemImpl;
begin
  TMII := TMenuItemImpl.Create(FAppIntf, FLinkedMenuItem);
  try
    if Assigned(FOnMenuClick) then
      FOnMenuClick(TMII);
  finally
    TMII.Free;
  end;
end;
Wenn der Menüpunkt angeklickt wird, erstellt diese Methode einen Wrapper auf den Menüpunkt, ruft die OnClick Methode des Plug-Ins auf und zerstört anschließend das Wrapper-Objekt wieder. Damit wird dem Plug-In die Möglichkeit geboten auf Klick im Menü zu reagieren.

Die Plug-Ins (I)

Das erste Plug-In ist wieder ein Non-Sense Plug-In, dafür aber sehr einfach. Es erstellt im Hauptmenü einen neuen Menüpunkt und erstellt darunter drei Einträge. Wenn einer dieser Einträge ausgewählt wird, wird eine MessageBox angezeigt, welche den Namen des angeklickten Menüpunkt nennt.
Delphi-Quellcode:
procedure TPlugInObject01.OnMenuClick(Sender: TIMenuItem);
begin
  Parent.ShowMessage(Format('"%s" was clicked', [Sender.GetCaption]));
end;
Die Plug-Ins (II)

Das zweite Plug-In ist etwas sinnvoller. Es stellt zwei neue Menüpunkt im File Menü zur Verfügung. "Alle Dokumente speichern" und "Alle Dokumente schließen". Nach Ausführung teilt es dem User mit, ob die Anfrage erfolgreich durchgeführt wurde.

Die Plug-Ins (III)

Das dritte Plug-In ist ein wenig komplexer. Es ermöglicht dem User die ShortCuts sämtlicher Menüpunkte neu zu definieren! Eigentlich eine echt geile Funktion. Nur das Programm brauch die eigentlich nicht ;-)

Die Einstellungen werden in einer INI Datei im Verzeichnis, in welchem auch die DLL liegt, gespeichert. Dadurch kann das Plug-In die Einstellungen beim nächsten Programmstart auch wiederherstellen ;-) Dieses Beispiel zeigt auch, wie man ein VCL-Form in eine DLL einbinden kann - und gleich ist die 350 KB größer als die anderen :shock:

Viel Spaß

Und nicht vergessen :!: Schaut in die About-Box, dort wird noch der Umgang mit der THeaderControl gezeigt :mrgreen:

...:cat:...

Matheridon 14. Nov 2003 10:21

Re: PlugIns in eigenen Anwendungen
 
Hi,

kann es sein dass du in den kompilierten Programmen irgendetwas falsch gemacht hast? Bei mir werden nämlich keine DLLs geladen, es kommt auch keine Fehlermeldung... beide Programme sagen mir einfach nur dass keine DLLs geladen wurden (und ja, die DLLs sind im richtigen Verzeichnis *g*)

Gruß,
Matheridon

sakura 14. Nov 2003 15:13

Re: PlugIns in eigenen Anwendungen
 
Genau habe ich es jetzt nicht im Kopf, aber sofern ich mich erinnere, müssen die DLLs in der Registry angegeben werden - genaueres steht auf jeden Fall im obigen Text.

...:cat:...

CalganX 2. Jan 2004 17:41

Re: PlugIns in eigenen Anwendungen
 
Für Interssierte:
Hier hat jbg mir einige Erklärungen gegeben, wie man Interface-Plugins erstellt. Das ist ein ganz normaler Beitrag, daher ist nicht alles so schlüssig, wie hier im Tutorial, allerdings denke ich dass dort (zwar an einem konkretem Beispiel aber dennoch) gut erklärt wird, wie das in etwas funktioniert.

Chris

FriFra 19. Jan 2004 01:03

Re: PlugIns in eigenen Anwendungen
 
Ich hab grosse Probleme bei der Umsetzung... Beide Beispiele laufen zwar bei mir, aber wenn ich den "Plugin-Code" in mein fertiges Projekt (ich hätte auch gern eine Plugin Schnittstelle) integriere, werde ich von "ungültigen Zeigeroperationen" bomardiert...
wenn ich Application.onexception abfange sehe ich zwar die Fehlermeldungen nicht mehr und es funktioniert auch alles, aber wo zum Teufel kommen diese Fehler her?

sakura 19. Jan 2004 08:39

Re: PlugIns in eigenen Anwendungen
 
Ohne Code schwer zu raten ;-) Aber bitte einen eigenen Thread für dieses Problem. Unter Umständen hast Du in einem der beiden Teile (Anwendung - PlugIn) vergessen ShareMem als erstes in die Projekt-Uses-Klausel aufzunehmen.

...:cat:...

nitschchedu 24. Apr 2006 08:41

Re: PlugIns in eigenen Anwendungen
 
Ich habe auch noch ein problem! Ich habe das Gerüst wie oben benutzt und noch zusätzlich eine funktion reingemacht wo ich auf einen TForm zukreifen will, der Befehl Show geht, aber wenn ich nun TControlls verschieben will in meine Anwendung, bekomme ich den Fehler TFont kann nicht TFont zugewissen werden!
Wie kann ich das beheben? :gruebel:

Ich kan euch auch mal den Code Posten.

FriFra 24. Apr 2006 09:06

Re: PlugIns in eigenen Anwendungen
 
schau mal nach, ob die TFont's aus der selben unit kommen... Das Problem mit dem TFont hatten wir hier auch schonmal irgendwo :gruebel:

nitschchedu 24. Apr 2006 09:34

Re: PlugIns in eigenen Anwendungen
 
Na das Problem ist das es nicht aus der Selben Unit kommt.
Ich habe eine Unit vom Hauptprogramm und eine Unit in der Dll.
Nun Create ich in der dLL das Form und versuche die Controlls ins Hauptprogramm zu laden, genau da passiert der Fehler. :(

himitsu 24. Apr 2006 09:45

Re: PlugIns in eigenen Anwendungen
 
Tja, dann kannst du es vergessen, solange du nur Delphi's MM benutzt.

Ließ dich mal da etwas durch ^^

xaromz 24. Apr 2006 09:49

Re: PlugIns in eigenen Anwendungen
 
Hallo,
Zitat:

Zitat von nitschchedu
Na das Problem ist das es nicht aus der Selben Unit kommt.
Ich habe eine Unit vom Hauptprogramm und eine Unit in der Dll.
Nun Create ich in der dLL das Form und versuche die Controlls ins Hauptprogramm zu laden, genau da passiert der Fehler. :(

Das sieht mir nach dem klassischen Problem aus, dass Du in beiden Projekten (HauptProgramm und DLL) die VCL einkompiliert hast. Leider hat jedes Programm seine eigene RTTI. Deshalb kann Hauptprogramm.TFont niemals DLL.TFont sein, auch wenn beide eigentlich die gleiche Klasse sind. Daher kommt auch der Fehler, TFont.Assign prüft, ob wirklich ein TFont übergeben wurde.
Die Lösung ist einfach: Packages verwenden.

Gruß
xaromz

nitschchedu 24. Apr 2006 09:54

Re: PlugIns in eigenen Anwendungen
 
Und wie sieht es mit Frames aus? Das ich das Frame aus der DLL im Hauptprogramm anzeigen lasse. Da wiedrum hatte ich auch das Problem!
Also wie ich es mache es kommt der Fehler :?. Ansonsten versuche ich es mal mit PAckets.

xaromz 24. Apr 2006 10:02

Re: PlugIns in eigenen Anwendungen
 
Hallo,
Zitat:

Zitat von nitschchedu
Und wie sieht es mit Frames aus? Das ich das Frame aus der DLL im Hauptprogramm anzeigen lasse. Da wiedrum hatte ich auch das Problem!
Also wie ich es mache es kommt der Fehler :?. Ansonsten versuche ich es mal mit PAckets.

Es ist völlig egal, welche Komponenten Du verwendest. Das Mischen von VCL-Komponenten in verschiedenen Programmen ist (ohne massiven Aufwand) nur mittels Packages zu lösen.

Gruß
xaromz

nitschchedu 24. Apr 2006 10:14

Re: PlugIns in eigenen Anwendungen
 
Aber ... Aber .. :cry: die machen das doch auch so http://www.delphi-forum.de Frame das möchte ich auch machen! Das muss doch gehen :?

xaromz 24. Apr 2006 10:27

Re: PlugIns in eigenen Anwendungen
 
Hallo,
Zitat:

Zitat von nitschchedu
Aber ... Aber .. :cry: die machen das doch auch so http://www.delphi-forum.de Frame das möchte ich auch machen! Das muss doch gehen :?

Wenn Du Dir den zweiten Beitrag im DF ansiehst, da steht:
Zitat:

Zitat von delphi-forum
ich habe es nun mit dem Frame hinbekommen, aber leider nur, wenn ich die DLL statisch lade und die Projekte (Hauptprogramm + DLL mit Laufzeitp. compiliere)

Laufzeitp. ist das Stichwort. das p. steht für Packages.

Gruß
xaromz

nitschchedu 24. Apr 2006 10:30

Re: PlugIns in eigenen Anwendungen
 
Ok ok dann gieb mir mal ne link für Pakecket.

xaromz 24. Apr 2006 10:39

Re: PlugIns in eigenen Anwendungen
 
Hallo,
Zitat:

Zitat von nitschchedu
Ok ok dann gieb mir mal ne link für Pakecket.

nix für ungut, aber das kommt jetzt etwas komisch :? . Ich bin doch keine Suchmaschine.
Wenn Du eine spezifische Frage hast, dann mach am Besten einen neuen Thread auf. Das Ganze hat in diesem eigentlich eh nichts zu suchen.
Um Dir aber den Anfang zu erleichtern: Unter Projekt -> Optionen -> Packages wählst Du unten "Laufzeit-Packages" aus. Beim Weitergeben Deiner Anwendung musst Du aber darauf achten, dass Du die Packages mitlieferst.

Gruß
xaromz

nitschchedu 24. Apr 2006 10:43

Re: PlugIns in eigenen Anwendungen
 
Ok sorry dachte da du davon anfängst das auch gleich einen Link hast Danke erstmal für alles :)

nitschchedu 4. Mai 2006 11:07

Re: PlugIns in eigenen Anwendungen
 
Noch mal ne frage ! Kann ich das wirklich nicht in DLL machen? Packges sind doch scheiße !
Kann von mir aus auch ein massiver Aufwand werden. Hauptsache es wird so das es Ordentlich ist und Funktioniert. Wäre Tool wenn es eine Möglichkeit gibt. :(

FriFra 4. Mai 2006 11:11

Re: PlugIns in eigenen Anwendungen
 
Es geht schon alles mit dlls zu lösen. Du musst Dir nur mal Gedanken über deine Schnittstellen machen... übergib z.B. keine TFont variable, sondern nur deren Parameter als PChar und Integer...

nitschchedu 4. Mai 2006 12:18

Re: PlugIns in eigenen Anwendungen
 
Sage mal kann ich nicht eigentlich ne Pointer auf die Adresse übergeben ?

Luckie 4. Sep 2007 10:33

Re: PlugIns in eigenen Anwendungen
 
Ich erstelle in dem Plugin ein Fenster und zeige es mit Show an.
Delphi-Quellcode:
procedure TPlugin01.Execute(XMLString: string);
begin
  inherited;
  frmTestPlugin01 := TfrmTestPlugin01.Create(nil);
  frmTestPlugin01.Label1.Caption := 'Testzeichenfolge: ' + XMLString;
  frmTestPlugin01.Label2.Caption := 'Parenthandle: ' + IntToStr(Self.Parent);
  frmTestPlugin01.Show;
end;
Freigegeben wird das Formular im OnClose-Ereignis des Formulares selber:
Delphi-Quellcode:
procedure TfrmTestPlugin01.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Self.Free;
end;
Aber wie kann ich jetzt zum einen die Plugin DLL benachrichtigen dass das Fenster geschlossen wurde und wie kann das Plgin die Hostanwendung benachrichtigen? Wie man Nachrichten an das Plugin schickt wird ja im Tutorial demonstriert, aber wie geht der umgekehrte Weg?

FriFra 4. Sep 2007 10:40

Re: PlugIns in eigenen Anwendungen
 
Also ich zeige meine Plugin-Fenster immer modal, dann stellt sich mir diese Frage nicht ;) ... ansonsten müsstest Du onClose die entspr. handles prüfen und diese freigeben.

mirage228 4. Sep 2007 12:07

Re: PlugIns in eigenen Anwendungen
 
Du könntest dem Plugin ein "Kommunikation"-Interface(oder -Klasse) beim Laden übergeben, über das die Kommunikation zur Anwendugn laufen könnte...

mfG
mirage228

Luckie 4. Sep 2007 13:41

Re: PlugIns in eigenen Anwendungen
 
Na ja, das erste problem besteht schon darin, wie die dpr-Datei der Plugin-DLL das Schliessen des Fensters mitbekommt. Hier wird es erstellt und angezeigt:
Delphi-Quellcode:
procedure TPlugin01.Execute(XMLString: string);
begin
  inherited;
  frmTestPlugin01 := TfrmTestPlugin01.Create(nil);
  frmTestPlugin01.Label1.Caption := 'Testzeichenfolge: ' + XMLString;
  frmTestPlugin01.Label2.Caption := 'Parenthandle: ' + IntToStr(Self.Parent);
  frmTestPlugin01.Show;
end;
Wenn das Formular geschlossen wird, soll auch die Host-Anwendung darüber benachrichtigt werden und das Plugin entladen.

Ich verwende, wie im Tutorial eine Plugin-Klasse, die der Host-Anwendung und dem Plugin bekannt ist:
Delphi-Quellcode:
type
  TEventNotification = (evnClose);
  TEventAction = (evnContinue, evnAbort);

  TPlugin = class(TObject)
  private
    FParent: THandle;
    procedure SetParent(const Value: THandle);
  public
    constructor Create(aParent: THandle);
    function GetName: string; virtual; stdcall; abstract;
    function GetAuthor: string; virtual; stdcall; abstract;
    function GetComment: string; virtual; stdcall; abstract;
    function GetCaption: string; virtual; stdcall; abstract;
    procedure Execute(XMLString: string); virtual; stdcall; abstract;
    function EventHandler(EventNotification: TEventNotification): TEventAction; virtual; stdcall; abstract;
    property Parent: THandle read FParent write SetParent;
  end;

  TLoadPlugin = function(Parent: THandle; var PlugIn: TPlugIn): Boolean;

implementation

{ TPlugIn }

constructor TPlugIn.Create(aParent: THandle);
begin
  inherited Create;
  FParent := aParent;
end;

procedure TPlugIn.SetParent(const Value: THandle);
begin
  FParent := Value;
end;

mirage228 4. Sep 2007 13:48

Re: PlugIns in eigenen Anwendungen
 
Also um das "OnClose"-Event des Forms wirst du erstmal nicht drum herum kommen.
Ich schlage vor:

1.) Im OnClose des Form wird TPlugin über das Schließen benachrichtigt (TPlugin.Notify(Sender: TObject; Action: TMyPluginAction);
--> Dazu muss TPlugin irgendwo innerhalb des Plugin-Forms gespeichert werden (nen eigenes Property, im Tag des Forms - wie auch immer!)
--> Bei TMyPluginAction kannst Du ja noch weitere Ereignisse reinpacken -- oder du machst statt "Notify" eine "TPlugin.PluginFormClosed(Sender: TObject)"-Methode
2.) Die Anwendung überigbt eine Klasse oder einen Methodenzeiger, den dein TPlugin-Objekt speichert und aufruft, sobald ein Ereignis (siehe oben) eingetreten ist.

Wenn ich alles richtig bedacht habe, sollte das so gehen :)

mfG
mirage228

Luckie 6. Sep 2007 15:57

Re: PlugIns in eigenen Anwendungen
 
Wenn ich die Sourcen zum zweiten Teil mit Delphi 6 kompilieren will, bekomme ich folgende fehlermeldungen:
Code:
[Fehler] uFormMain.pas(88): Ausdruckstyp muß BOOLEAN sein
[Fehler] uFormMain.pas(94): Ausdruckstyp muß BOOLEAN sein
[Fehler] uFormMain.pas(100): Ausdruckstyp muß BOOLEAN sein
[Fehler] uFormMain.pas(112): Anweisung erwartet, aber 'PROCEDURE' gefunden
[Fehler] uFormMain.pas(39): Ungenügende Forward- oder External-Deklaration: 'TfrmMain.mnuFileCloseClick'
[Fehler] uFormMain.pas(43): Ungenügende Forward- oder External-Deklaration: 'TfrmMain.mnuEditCutClick'
[Fehler] uFormMain.pas(45): Ungenügende Forward- oder External-Deklaration: 'TfrmMain.mnuEditPasteClick'
[Fataler Fehler] ExtendableApp.dpr(10): Verwendete Unit 'uFormMain.pas' kann nicht compiliert werden
Das wären diese Zeilen:
Delphi-Quellcode:
procedure TfrmMain.mnuEditCopyClick(Sender: TObject);
begin
  if CurrentDocument <> nil then
    if CurrentDocument.mmoDocument.CopyToClipboard;
end;

procedure TfrmMain.mnuEditCutClick(Sender: TObject);
begin
  if CurrentDocument <> nil then
    if CurrentDocument.mmoDocument.CutToClipboard;
end;

procedure TfrmMain.mnuEditPasteClick(Sender: TObject);
begin
  if CurrentDocument <> nil then
    if CurrentDocument.mmoDocument.PasteFromClipboard;
end;
Jeweils die zweite if-Bedingung.

Delphi-Quellcode:
procedure TfrmMain.mnuFileCloseClick(Sender: TObject);
begin
  if CurrentDocument <> nil then
    if CurrentDocument.CloseDocument then
      ShowMessage('yes')
    else
      ShowMessage('no');
end;

procedure TfrmMain.mnuFileExitClick(Sender: TObject); // <- Zeile 112
begin
  Close;
end;

Die Muhkuh 6. Sep 2007 16:17

Re: PlugIns in eigenen Anwendungen
 
Ich würde sagen, dass einfach das IF weggehört. Vielleicht ein Tippfehler? :gruebel:

Luckie 6. Sep 2007 21:20

Re: PlugIns in eigenen Anwendungen
 
Das habe ich mir auch so edacht. Aber ih dachte, dass sakura das zu mindest testweise noch mal kompiliert hätte.

viakt133 26. Feb 2010 21:14

Re: PlugIns in eigenen Anwendungen
 
Hallo,

ich beschäftige mich grad mit Modularisierung per Interface und da hab ich diesen Thread hier gefunden, der das was ich mit interfaces erreichen will, mit Plugins macht.

Habe die Quelltexte auf meinen Rechner geladen. Aber irgendwas mach ich noch falsch. Die Plugins der Beispielprogramme werden nicht geladen.

Ich habe folgendes probiert:

im Ordner ../ExtendibleApplication (I)/Bins/plugins.ini

[sample01]
;type=Plugin(off) auskommentiert
dllname=plugin01.dll

[sample01]
type=Plugin(off) ganz gelöscht
dllname=plugin01.dll

und

[sample01]
type=Plugin() nur das Wort "off" entfernt
dllname=plugin01.dll

[sample01]
type=Plugin(on) das Wort "off" entfernt und durch "on" ersetzt.
dllname=plugin01.dll


Trotzdem wird kein Plugin geladen.

Ich erhalte bei Anklicken des Menüs Plug-ins->Show Plug-in Information die Meldung

"NO OF PLUG-INS LOADED: 0"

Was muss ich ändern. Eventuell muss ja der Quellcode korrigiert werden.
Bisher habe ich nur die .exe getestet.

Wo muss ich evtl. im Ouelltext nachsehen?

Wo gibt es weitere Dokus zum Thema?

viakt133 26. Feb 2010 21:36

Re: PlugIns in eigenen Anwendungen
 
Zitat:

Zitat von sakura
Genau habe ich es jetzt nicht im Kopf, aber sofern ich mich erinnere, müssen die DLLs in der Registry angegeben werden - genaueres steht auf jeden Fall im obigen Text.

...:cat:...

Up's, das hab ich vorhin leider überlesen: Sorry! Wie war gleich das Kommando, um ne Dll in der Registry anzumelden?

RWarnecke 26. Feb 2010 22:01

Re: PlugIns in eigenen Anwendungen
 
Hallo,

Du brauchst die DLL's nicht in der Registry anmelden um Sie als Plugin nutzen zu können. Das geht aus so.

Aber den Befehl den Du wahrscheinlich suchst heißt regsvr32.exe

Luckie 26. Feb 2010 22:05

Re: PlugIns in eigenen Anwendungen
 
COM DLLs muss man im System registrieren, aber keine DLL Plugins für Anwendungen.

viakt133 26. Feb 2010 22:12

Re: PlugIns in eigenen Anwendungen
 
Zitat:

Zitat von RWarnecke
Hallo,

Du brauchst die DLL's nicht in der Registry anmelden um Sie als Plugin nutzen zu können. Das geht aus so.

Aber den Befehl den Du wahrscheinlich suchst heißt regsvr32.exe

Danke, wie verrückt. Das war der Befehl, den ich gesucht habe. :smile2:

Jetzt hab ich auch rausgekriegt wie die entscheidende Zeile in der Ini Datei heißen muss:

[sample0X]
type=plugin
dllname=<Name der zu ladenden DLL>

Ich hatte ja die Zeile mit der Plugin Angabe ganz gelöscht oder plugin(on) geschrieben. Damit klappte es nicht. Nur so, wie hier mit Sample0X angegeben funktioniert es. :hi:


.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:13 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