Einzelnen Beitrag anzeigen

Benutzerbild von TheMiller
TheMiller

Registriert seit: 19. Mai 2003
Ort: Gründau
2.480 Beiträge
 
Delphi XE7 Architect
 
#1

Mehrere DLL-Forms (Plugins) gleichzeitig öffnen

  Alt 17. Jul 2012, 10:46
Delphi-Version: 2009
Hallo,

ich verzweifel mal wieder etwas an DLL-Forms, obwohl ich sie eigentlich recht gut im Griff habe - denke ich jedenfalls. Bevor ich den Quelltext poste, erst einmal eine kleine Fehlerbeschreibung:

Ich lade ein Plugin, entlade es wieder, lade es wieder, entlade es wieder usw. Irgendwann kommt die Meldung "[Programmname] funktioniert nicht mehr". Dabei stoppt der Debugger nicht. Das alles passiert aber nur, wenn mehrere Plugins im Spiel sind. Ist nur eins eingebunden, dann kann ich das so lange machen, bis ich umkippe.

Damit sich die VCL nicht stören, habe ich Methoden in die DLL eingefügt, die die Form erstellen, anzeigen und wieder freigeben. Wenn ich Strings übergebe, dann nur über den Umweg des Buffers (lasse mir die Länge des Strings ausgeben, reserviere Speicher und kopiere mir den String mit der Länge "Buffer" aus dem Speicher). Daher habe ich auch keine gemeinsame Speicherverwaltung, außer zu Debugzwecken FastMM4

Probleme treten nur beim Entladen mit FreeLibrary auf und auch nur dann, wenn ich die Methoden zum Erstellen|Anzeigen|Freigeben von Forms aufrufe. Rufe ich sie einfach nicht auf, scheint alles zu funktionieren.

Der Hauptfehler tritt dann bei Methode "UnloadPlugins" in der Nähe/oder bei "FreeLibrary" auf.

Ich hoffe, ihr könnt mir trotz der ungenauen Fehlerbeschreibung helfen und habt Lust, euch durch den Quelltext zu wühlen. Ich habe die wichtigsten Stellen gepostet und nochmal näher dokumentiert. Die Reihenfolge der Quelltexte ist vom Ablauf systematisch, also Einlesen, Öffnen, Schließen und dann die DLL.


Typdeklaration: TPlugins (ObjectList, die alle Plugins hält
Delphi-Quellcode:
  TPlugins = class(TObjectList)
  private
    function GetItem(Index: Integer): TPlugin;
  public
    property Plugin[Index: Integer]: TPlugin read GetItem; default;
  end;
Typdeklaration: TPlugin (Enthält Infos über das Plugin, sowie das Plugin selbst, wenn es geladen wurde)
Delphi-Quellcode:
  TPlugin = class(TObject)
  private
    fActive: Boolean;
    fFilename: String;
    fHandle: Cardinal;
    fFormHandle: Cardinal;
    fImageIdx: Integer;
    fPlugin: TxPlugin;
    fRechte: Boolean;
    fTitel: String;
    fUMID: String;
  public
    property Active: Boolean read fActive write fActive;
    property Filename: String read fFilename write fFilename;
    property FormHandle: Cardinal read fFormHandle write fFormHandle;
    property Handle: Cardinal read fHandle write fHandle;
    property ImageIdx: Integer read fImageIdx write fImageIdx;
    property Plugin: TxPlugin read fPlugin write fPlugin;
    property Rechte: Boolean read fRechte write fRechte;
    property Titel: String read fTitel write fTitel;
    property UMID: String read fUMID write fUMID;
  end;
Einlesen der installieren Plugins, danach wieder entladen

Delphi-Quellcode:
procedure TForm1.InstallPlugins;
var
  sr: TSearchRec;
  dir, ext: String;
  TempHandle: Cardinal;
  ProcAddr: Pointer;
  LoadPlugInProc: TLoadPlugin;
  TempPlugin: TxPlugin;
  i,j: Integer;
  titel, umid: String;
  Buffer: PChar;
  Plugin: TPlugin;
  TreeItem: TTreeNode;
begin
  if (Plugins = nil) then
    Plugins:=TPlugins.Create;

  Plugins.Clear;

  Dir:=ExtractFilePath(ParamStr(0))+'PlugIns\';
  Ext:='*.dll';
  if FindFirst(Dir+Ext, faAnyFile - faDirectory, sr) = 0 then
  begin
    try
      repeat

        //Plugin über export-Fkt. laden via LoadLibrary
        //TempPlugin.Handle enthält das von LoadLibrary angegebene Handle
        TempPlugin:=LoadPlugin(Dir+Sr.Name);

        //Gathering Plugin-Titel (Unicode-String "übergeben")
        i:=(TempPlugin.GetTitel(nil, 0))*2+2;
        try
          GetMem(Buffer, i);
          TempPlugin.GetTitel(Buffer, i);
          titel:=Buffer;
        finally
          FreeMem(Buffer);
        end;

        //Gathering UMID (Unicode-String "übergeben")
        i:=(TempPlugin.GetGUID(nil, 0))*2+2;
        try
          GetMem(Buffer, i);
          i:=TempPlugin.GetGUID(Buffer, i);
          umid:=Buffer;
        finally
          FreeMem(Buffer);
        end;

        Plugin:=TPlugin.Create; //Plugin-Objekt "komfortables Behältnis" erstellen
        Plugin.Plugin:=TempPlugin;
        Plugins.Add(Plugin); //Behältnis zur Plugin-Liste hinzufügen
        Plugin.Active:=True; //Es wird gerade ausgeführt.
        Plugin.Filename:=Dir+sr.Name;
        Plugin.ImageIdx:=Plugin.Plugin.PngImgIndex; //Integer
        Plugin.Titel:=titel; //String
        Plugin.UMID:=umid;
        Plugin.Rechte:=False; //Hat der User Rechte, das Plugin zu nutzen?
        UnloadPlugin(Plugin.Titel); //Plugin wieder entladen. Installation fertig.

      until
        FindNext(sr) <> 0
    finally
      FindClose(sr);

    end;
  end;
  TreeView1.Items.Item[0].Expand(True);
end;
Benutzen eines Plugins
Delphi-Quellcode:
  //Das zu öffnende Plugin in der Plugins-Liste suchen
  //Dann wieder (wie bei InstallPlugins) mit LoadPlugin (=> LoadLibrary) laden
  //Forms von DLL erstellen lassen
  //Forms von DLL anzeigen lassen
  if (TreeView1.Selected <> nil) then
  begin
    for j:=0 to Plugins.Count-1 do
    begin
      umid:=String(TreeView1.Selected.Data);
      if (Plugins.Plugin[j].UMID = umid) then
      begin
        Plugins.Plugin[j].Plugin:=LoadPlugin(Plugins.Plugin[j].Filename);
        Plugins.Plugin[j].Active:=True;

        Plugins.Plugin[j].Plugin.GetMfHandle(Self.Handle);
        Plugins.Plugin[j].Plugin.CreateForms; <-- Erstellt die Forms
        //Plugins.Plugin[j].FormHandle:=Plugins.Plugin[j].Plugin.SendFormHandle;
        Plugins.Plugin[j].Plugin.GetUID(Benutzer.UserID);
        Plugins.Plugin[j].Handle:=Plugins.Plugin[j].Plugin.Handle;

        //Plugins's "Start-UP" aufrufen
        Plugins.Plugin[j].Plugin.Execute;
      end;
... ...
Das Plugin wieder beenden
Delphi-Quellcode:
procedure TForm1.UnloadPlugIn(modulName: string);
var
  i: Integer;
begin
  for i:=0 to Plugins.Count-1 do
  begin
    if (Plugins.Plugin[i].Titel = Trim(Modulname)) then
    begin
      if (Plugins.Plugin[i].Active) then
      begin
        Plugins.Plugin[i].Plugin.FreeForms;
        Plugins.Plugin[i].Active:=False;
        FreeLibrary(Plugins.Plugin[i].Plugin.Handle);
      end;
    end;
  end;
end;
Und nun noch die Methoden aus der DLL zum Erstellen von Freigeben der Forms:

Delphi-Quellcode:
procedure TPlugin1.CreateForms;
begin
  Form1:=TForm1.Create(nil);
end;

procedure TPlugin1.Execute();
begin
  if (Form1 <> nil) then
    Form1.StartUp;
end;

procedure TPlugin1.FreeForms;
begin
  if (Form1 <> nil) then
  begin
    Form1.Free;
    //Form1.Release <-- erzeugt (mehr) Fehler bei FreeLibrary
  end;
end;

function TPlugin1.GetTitel(Buffer: PChar; lenBuffer: Integer): Integer;
var
  s: String;
begin
  //Danke an Luckie für sein gutes DLL-Strings-Tutorial
  s:='Adressen';
  if Assigned(Buffer) then
  begin
    StrLCopy(Buffer, PChar(s), lenBuffer);
  end;
  result:=length(s);
end;

procedure TPlugin1.SwitchShowStyle;
begin
  if (Form1 <> nil) then
  begin
    if (Form1.Visible = true) then
      Form1.Hide
    else
      Form1.Show;
  end;
end;
Ich habe echt viel darüber gelesen, arbeite schon länger mit DLLs und habe darauf geachtet, dass ich gegen keine "Regel" verstoße (VCLs, Speichermanager). Aber anscheinend habe ich irgendwo was übersehen oder wusste generell was noch nicht.
Vielen Dank im Voraus!
  Mit Zitat antworten Zitat