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!