Einzelnen Beitrag anzeigen

SMO

Registriert seit: 20. Jul 2005
178 Beiträge
 
Delphi XE6 Professional
 
#10

AW: Interface aus DLL mit dynamischer Bindung - wann FreeLibrary aufrufen?

  Alt 25. Aug 2015, 00:59
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.
  Mit Zitat antworten Zitat