Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen? (https://www.delphipraxis.net/186326-interface-aus-dll-mit-dynamischer-bindung-wann-freelibrary-aufrufen.html)

DeddyH 24. Aug 2015 14:25

Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Ich versuche mich gerade an einer Art Plugin-System. Dabei soll eine DLL ein Interface zurückgeben. Das sieht im Moment so aus:
Delphi-Quellcode:
library Blabla;

uses MyIntf, MyClass; //Units, in denen das Interface und die implementierende Klasse stehen

{$R *.res}

procedure GetDings(out Dings: IDings); stdcall; export;
begin
  Dings := TDings.Create;
end;

exports
  GetDings;

begin
end;
Allerdings soll es mehrere DLLs geben, die verschiedene IDings-Klassen zurückliefern, daher binde ich diese dynamisch (LoadLibrary, GetProcAddress). Klappt soweit, aber der ordentliche Mensch denkt sich: wer LoadLibrary sagt, muss auch FreeLibrary sagen. Bei einem ersten Versuch hat es nur dann keine AV gegeben, wenn ich dies in einen Klassendestruktor verfrachtet habe. Es soll aber auch möglich sein, verschiedene DLLs nacheinander anzusprechen, da würde ich mir auf diese Weise ja jedesmal das Handle überschreiben und könnte somit nur das letzte wieder freigeben. Wo ist denn mein Denkfehler bzw. was könnte ich vergessen haben? Lasse ich FreeLibrary ganz weg, fluppt alles super, aber verschwende ich so keine Ressourcen?

baumina 24. Aug 2015 14:36

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Ich merk mir die Handles in einer Liste, um sie dann mit FreeLibrary freigeben zu können.

Mavarik 24. Aug 2015 14:37

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Übergibt Deiner Interface erzeuge Routine eine Backcall für den FreeLibary

Wenn das Interface Refcount 0 ist, wird der Destructor aufgerufen und der ruft - als letztes - den FreeLib auf.
(Asynchron wegen dem Return auf den Aufrufer)!

Zacherl 24. Aug 2015 14:38

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Resourcen verschwenden würdest du zwar keine, wenn das FreeLibrary sowieso erst bei Beendigung deines Programms aufgerufen würde, aber mit ist es natürlich schon sauberer.

Ohne mehr Code kann ich leider nicht viel sagen, aber ich vermute mal, dass das implizierte _Release von Delphi dir da einen Strich durch die Rechnung macht. Hast du sichergestellt, dass deine Interface-Instanzen vor dem FreeLibrary jeweils einmal manuell auf nil gesetzt wurden? Ansonsten könnte es passieren, dass die DLL entladen wird und danach impliziert _Release (und damit der Destructor) der aus dem Scope-laufenden Objekte gecallt wird. Da zu diesem Zeitpunkt die DLL dann schon entladen ist, gibt es an dieser Stelle die AV.

DeddyH 24. Aug 2015 14:51

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Ich bau mal ein Beispiel.

DeddyH 24. Aug 2015 15:15

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
War ja klar: im Beispiel funktioniert alles :wall:, der Fehler muss also an einer anderen Stelle liegen. Ich forsche mal weiter.

Zacherl 24. Aug 2015 15:38

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Du könntest meine Theorie relativ einfach testen, indem du im Destructor deiner Objekte (die aus der DLL) mal eine MessageBox einbaust. Dann ersetzt du das FreeLibrary durch eine weitere MessageBox.

Erscheint jetzt die FreeLibrary Message vor der Message aus dem Destructor, weißt du, dass irgendwo noch Objektinstanzen existieren, die vor dem FreeLibrary nicht freigegeben werden.

DeddyH 24. Aug 2015 16:00

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Hab ich gemacht, die Reihenfolge stimmt: zuerst werden die DLL-Objekte freigegeben, danach käme der FreeLibrary-Aufruf. Mittlerweile ist es genau andersherum als eingangs geschildert: wenn ein neuer DLL-Name zugewiesen wird, klappt ein Nullen des alten Objektes mit anschließendem FreeLibrary einwandfrei, nur im Destruktor nicht mehr. Das alles aber nur im echten Projekt, das Referenzbeispiel funktioniert ohne Murren.

DeddyH 24. Aug 2015 19:33

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, zu Hause nochmal unter XE7 nachgebaut und schon den ersten Fehler gefunden (Verwendung von string statt Widestring). Aber auch hier klappt es nur, wenn ich das FreeLibrary auskommentiere. Ich habe den Verdacht, dass ich eine ganz entscheidende Kleinigkeit vergessen habe (das ist zugegebenermaßen mein allererster Versuch in dieser Richtung). Ich hänge meine Demo mal an, jemand mit mehr Erfahrung auf dem Gebiet sieht das evtl. sofort.

SMO 25. Aug 2015 00:59

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?
 
Delphi-Quellcode:
procedure TfrmMain.btnLoadClick(Sender: TObject);
type
  TLoadProc = procedure(out Person: IPerson); stdcall;
var
  DLLHandle: NativeUInt;
  PersonProc: TLoadProc;
  Person: IPerson;
  Item: TListItem;
begin
// ...
              while not Person.Eof do
                begin
                  Item := lvChildren.Items.Add;
                  Item.Caption := Person.Child.Name;
                  Item.SubItems.Add(DateToStr(Person.Child.Birthdate));
                  Person.Next;
// ...
    finally
      Person := nil;
      FreeLibrary(DLLHandle);
    end;
end;

Das Problem ist folgendes:
Person.Child liefert ein Interface (IChild). Das wird hier aber nicht in einer lokalen Variable gespeichert, sondern es wird gleich auf eine Eigenschaft zugegriffen (Name, Birthdate). Damit die automatische Referenzzählung funktioniert, muss Delphi aber trotzdem jede Interface-Instanz in einer unsichtbaren lokalen Variable speichern (hier also 2x IChild), die dann am Ende der Subroutine in einem impliziten "finally" Block freigegeben werden.
FreeLibrary wird aber vorher ausgeführt, d.h. es wird auf Ressourcen zugegriffen, die nicht mehr verfügbar sind. Daher die Zugriffsverletzung.

Lösung: Person.Child explizit in einer Variable speichern und die Referenz ebenfalls vor FreeLibrary freigeben.

Delphi-Quellcode:
procedure TfrmMain.btnLoadClick(Sender: TObject);
type
  TLoadProc = procedure(out Person: IPerson); stdcall;
var
  DLLHandle: NativeUInt;
  PersonProc: TLoadProc;
  Person: IPerson;
  Child: IChild;
  Item: TListItem;
begin
// ...
              while not Person.Eof do
                begin
                  Item := lvChildren.Items.Add;
                  Child := Person.Child;
                  Item.Caption := Child.Name;
                  Item.SubItems.Add(DateToStr(Child.Birthdate));
                  Person.Next;
// ...
    finally
      Person := nil;
      Child := nil;
      FreeLibrary(DLLHandle);
    end;
end;

Falls die DLL übrigens erkennen soll, wann sie geladen oder freigegeben wird, dann geht das ungefähr so.


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:55 Uhr.
Seite 1 von 2  1 2      

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