Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi TObject, DLL, DLL Freigabe (https://www.delphipraxis.net/179544-tobject-dll-dll-freigabe.html)

haentschman 14. Mär 2014 10:48


TObject, DLL, DLL Freigabe
 
Hallo Alle... 8-)

Der Titel ist nur eine Stichwortsammlung. :roll:

1. Gegeben:
Datenobjekte (unwichtiges entfernt)
Delphi-Quellcode:
TMasterDeviceVersion = class
  strict private
  var
    FModel: string;
    FAppVersion: string;
    FSysVersion: string;
  public
    property Model: string read FModel write FModel;
    property AppVersion: string read FAppVersion write FAppVersion;
    property SysVersion: string read FSysVersion write FSysVersion;
  end;

TMasterDevice = class
  strict private
    FVersion: TMasterDeviceVersion;
  public
    constructor Create;
    destructor Destroy; override;
    property Version: TMasterDeviceVersion read FVersion write FVersion;
    ...
  end;
.
.
constructor TMasterDevice.Create;
begin
  FVersion:= TMasterDeviceVersion.Create;
end;

destructor TMasterDevice.Destroy;
begin
  FVersion.Free;
  inherited;
end;
2.Gegeben:
Auszug aus DLL
Delphi-Quellcode:
procedure ParseVersion(const aText: PChar; MasterVersion: TMasterDeviceVersion); stdcall;
var
  sl: TStringList;
  I, J: Integer;
begin
  sl:= TStringList.Create;
  try
    sl.Text:= aText;
    for I:= 0 to sl.Count - 1 do
    begin
      if AnsiStartsText('var verAp', sl[I]) then
      begin
        for J:= 12 to Length(sl[I]) do
        begin
          if sl[I][J] = ' ' then
          begin
            MasterVersion.Model:= Copy(sl[I], 12, J - 12);
            MasterVersion.AppVersion:= Copy(sl[I], J + 1, Length(sl[I]) - J - 2);
            Break;
          end;
        end;
      end;
      if AnsiStartsText('var verSist', sl[I]) then
      begin
        MasterVersion.SysVersion:= Copy(sl[I], 14, Length(sl[I]) - 15);
      end;
    end;
  finally
    sl.Free;
  end;
end;
Problem:
1. Die Instanz von TMasterDevice wird im Hauptthread erzeugt
2. Die DLL wird dynamisch geladen
3. Der DLL Procedure wird der "Text" und die MasterVersion übergeben.
4. Wenn die Procedure Parse Version zurückkommt sind alle Versions Properties korrekt gefüllt.
5. Wenn ich nun die DLL freigebe steht in MasterVersion nur Müll drin. Im Master Objekt, welches auch über die DLL gefüllt wird ist alles noch korrekt.

Kann mir einer die Ursache erklären?

Nachtrag:

Aufruf der Procedure aus der DLL
Delphi-Quellcode:
if LoadMessage.Data > '' then
begin
  FParseVersion(PChar(LoadMessage.Data), FMasterDevice.Version);
  if FMasterDevice.Version.AppVersion > '' then // hier ist korrekt gefüllt... Basis DLL geladen
  begin
    LoadMasterDeviceDLL; // gibt ein vorhandenes DLL Handle (Basis DLL) frei und lädt die passende DLL nach Version
  end;
  if Assigned(FOnVersion) then // hier steht dann Müll im FMasterDeviceVersion direkt nach der Freigabe in LoadMasterDeviceDLL
    FOnVersion(self, True);
end

mkinzler 14. Mär 2014 11:00

AW: TObject, DLL, DLL Freigabe
 
Wenn Du Delphi Objekte im Interface zwischen Hauptprogramm und Bibliothek verwenden willst darfst Du keine Dlls sondern musst Bpls verwenden. Oder du musst ein "flache" Schnittstelle verwenden.

haentschman 14. Mär 2014 11:23

AW: TObject, DLL, DLL Freigabe
 
Danke für deine Antwort.

Solange ich die DLL nur mit dem Delphi Programm benutze soll es keine Probleme geben. Es funktioniert ja auch, solange die DLL geladen bleibt. Da aber das Objekt im Hauptthread erzeugt wird verstehe ich nicht wieso die Freigabe der DLL den Inhalt beeinflußt. :gruebel:

TiGü 14. Mär 2014 12:42

AW: TObject, DLL, DLL Freigabe
 
Ich versuchs mal mit meinen bescheidenen Wissensstand:
Es sind zwei verschiedene Speichermanager!
Einer in der Anwendung, einer in der DLL.
Die Strings (AppVersion, Model...) unterliegen den Speichermanager der DLL.
Wenn die DLL entladen wird, werden auch alle referenzgezählten Typen darin freigeben, also die Strings!
Ergebnis: Du hast im MasterVersion-Objekt in deiner Anwendung zwar noch gültige Adressen auf irgendwelche Speicherbereiche, aber da steht jetzt was anderes bzw. Müll drin.

haentschman 14. Mär 2014 12:58

AW: TObject, DLL, DLL Freigabe
 
Danke erstmal 8-)
Zitat:

Die Strings (AppVersion, Model...) unterliegen den Speichermanager der DLL.
Ich glaube ja auch das es daran liegt. Aber wieso? Die gehören zu dem Objekt welches im Hauptthread erzeugt wurde. :gruebel: Liegt es daran, daß der Text über eine Stringlist aufbereitet wird welche in der DLL erzeugt wird? Da müßte aber schon beim Freigeben der Stringlist Müll drinstehen.

schöni 14. Mär 2014 13:10

AW: TObject, DLL, DLL Freigabe
 
Zitat:

Zitat von TiGü (Beitrag 1252001)
Ich versuchs mal mit meinen bescheidenen Wissensstand:
Es sind zwei verschiedene Speichermanager!
Einer in der Anwendung, einer in der DLL.
Die Strings (AppVersion, Model...) unterliegen den Speichermanager der DLL.
Wenn die DLL entladen wird, werden auch alle referenzgezählten Typen darin freigeben, also die Strings!
Ergebnis: Du hast im MasterVersion-Objekt in deiner Anwendung zwar noch gültige Adressen auf irgendwelche Speicherbereiche, aber da steht jetzt was anderes bzw. Müll drin.

Heißt das, das bei der BPL ein Speichermanager für alles zuständig ist, während bei der gewöhnlichen dll 2 verschieden sind. Ich diskutier gerade beim Lazaurs 1.2 Thread mit, dort geht es um die notwendige Neuübersetzung der IDE bei Installation neuer Komponenten. Ist also das Speichermanagerproblem einer der Gründe dafür?

@mkinzler: Was ist eine "flache Schnittstelle"?

mkinzler 14. Mär 2014 13:16

AW: TObject, DLL, DLL Freigabe
 
Zitat:

Zitat von schöni (Beitrag 1252008)
Zitat:

Zitat von TiGü (Beitrag 1252001)
Ich versuchs mal mit meinen bescheidenen Wissensstand:
Es sind zwei verschiedene Speichermanager!
Einer in der Anwendung, einer in der DLL.
Die Strings (AppVersion, Model...) unterliegen den Speichermanager der DLL.
Wenn die DLL entladen wird, werden auch alle referenzgezählten Typen darin freigeben, also die Strings!
Ergebnis: Du hast im MasterVersion-Objekt in deiner Anwendung zwar noch gültige Adressen auf irgendwelche Speicherbereiche, aber da steht jetzt was anderes bzw. Müll drin.

Heißt das, das bei der BPL ein Speichermanager für alles zuständig ist, während bei der gewöhnlichen dll 2 verschieden sind.

Ja.

Zitat:

@mkinzler: Was ist eine "flache Schnittstelle"?
Die einzelnen Teile gesondert. Bzw. über ein Interface

haentschman 14. Mär 2014 13:23

AW: TObject, DLL, DLL Freigabe
 
@mkinzler

Kannst du mal skizzieren wie ich eine DLL über ein Interface anbinde und keine "Speicher" Probleme habe. Wenn ich mir von der DLL das Interface auf die "Proceduren" geben lasse ist das Speicherproblem Geschichte?

jaenicke 14. Mär 2014 13:36

AW: TObject, DLL, DLL Freigabe
 
Im Grunde sehr einfach:
Delphi-Quellcode:
  IMasterDeviceVersion = interface
  ['{DF6962EE-B638-4B59-9BE6-82B8BB369477}']
    function GetAppVersion: WideString;
    function GetModel: WideString;
    function GetSysVersion: WideString;
    procedure SetAppVersion(const Value: WideString);
    procedure SetModel(const Value: WideString);
    procedure SetSysVersion(const Value: WideString);

    property Model: WideString read GetModel write SetModel;
    property AppVersion: WideString read GetAppVersion write SetAppVersion;
    property SysVersion: WideString read GetSysVersion write SetSysVersion;
  end;

  TMasterDeviceVersion = class(TInterfacedObject, IMasterDeviceVersion)
  strict private
    var
      FModel: WideString;
      FAppVersion: WideString;
      FSysVersion: WideString;
    function GetAppVersion: WideString;
    function GetModel: WideString;
    function GetSysVersion: WideString;
    procedure SetAppVersion(const Value: WideString);
    procedure SetModel(const Value: WideString);
    procedure SetSysVersion(const Value: WideString);
  public
    property Model: WideString read GetModel write SetModel;
    property AppVersion: WideString read GetAppVersion write SetAppVersion;
    property SysVersion: WideString read GetSysVersion write SetSysVersion;
  end;
Und dann:
Delphi-Quellcode:
procedure ParseVersion(const aText: PChar; MasterVersion: IMasterDeviceVersion); stdcall;
//
Du darfst dann natürlich auch in der Anwendung nur noch mit IMasterDeviceVersion usw. arbeiten.

haentschman 14. Mär 2014 13:42

AW: TObject, DLL, DLL Freigabe
 
Danke, das ist wirklich zu einfach. Warum nicht gleich so? :oops:

himitsu 14. Mär 2014 13:55

AW: TObject, DLL, DLL Freigabe
 
Jupp, bei Klassen mußt du auf BPLs wechseln.

Wennn du Interfaces nutzt und nicht willst, daß die DLL vor Freigabe des Interfaces freigegeben wird, dann könntest du in dem Objekt eine eigene Instanz der DLL holen.
Im Contructor ein MSDN-Library durchsuchenLoadLibrary.

Aber beim Freigeben gibt es ein "kleines" Problemchen, denn da müsstest du jetzt das FreeLibrary innerhalb der DLL aufrufen (im Destructor) aber dann würdest du ja den Coderücksprungpunkt löschen, auf den das MSDN-Library durchsuchenFreeLibrary zurück will, womit es dann kannlt, bevor die DLL komplett verlassen wurde.
Man kann da aber einen Thread starten, welcher etwas verzögert die DLL und sich frei gibt. MSDN-Library durchsuchenFreeLibraryAndExitThread (aber kein RTL-Thread, sondern nur eine "einfache" Thread-Prozedur, denn sonst bleiben eventuell Speicherlecks zurück ode es knallt dennoch, falls die DLL/RTL sich den Thread merkt und freigeben will, wenn sie freigegeben wird)

haentschman 14. Mär 2014 14:08

AW: TObject, DLL, DLL Freigabe
 
Nachfrage:

Ich hätte noch als Übergabeparameter zur DLL eine generische TObjectList. (hallo DLL, mach mal die Liste voll) Wie gehe ich damit um? Da die Objekte nun zu Interfaces geworden sind geht ja nur TList. Die Interfaces werden dann in der DLL erzeugt und in die Liste gelegt.

Welche Probleme erwarten mich? 8-)

Zitat:

Wennn du Interfaces nutzt und nicht willst, daß die DLL vor Freigabe des Interfaces freigegeben wird
...die Frage ist doch, was passiert mit dem Inhalt des Interfaces bzw. der Liste mit den Interfaces beim Entladen der DLL? Bleibt das erhalten?

himitsu 14. Mär 2014 15:24

AW: TObject, DLL, DLL Freigabe
 
Zitat:

Zitat von haentschman (Beitrag 1252021)
Zitat:

Wennn du Interfaces nutzt und nicht willst, daß die DLL vor Freigabe des Interfaces freigegeben wird
...die Frage ist doch, was passiert mit dem Inhalt des Interfaces bzw. der Liste mit den Interfaces beim Entladen der DLL? Bleibt das erhalten?

Das Interface geht "kaputt".

Der Speicher in der Liste wird vom Speichermanager der DLL freigegeben
und der Code, auf welchen die Funktionen des Interfaces zeigen ist weg da er ja in der DLL war.



Man könnte zwar absichtlich den Speichermanager zerschießen, damit er die "Speicherlecks" beim Beenden nicht freigibt,
oder man baut seine TList so, daß sie den Speicher z.B. direkt bei Windows holt und nicht über den Speichermanager geht,
oder man sorgt dafür, daß der Speichermanager nicht genutzt wird und stattdessen der Speichermanager der EXE verwendet wird (SharedMM),
aber es fehlt dann immernich der Code (alle Methoden, die man in dem Interface aufrufen kann), welcher die Liste verwaltet.

Die Liste müsste also in der EXE erstellt werden und alle Strings/Inhalte, dürfen nicht im Speichermanager der DLL liegen.
> Kein String, sondern nur WideString darf im Interface verwendet werden und auch sonst nichts, was im Speichermanader der DLL landen könnte.

jaenicke 14. Mär 2014 15:33

AW: TObject, DLL, DLL Freigabe
 
Zitat:

Zitat von haentschman (Beitrag 1252021)
Ich hätte noch als Übergabeparameter zur DLL eine generische TObjectList. (hallo DLL, mach mal die Liste voll) Wie gehe ich damit um? Da die Objekte nun zu Interfaces geworden sind geht ja nur TList.

Würde ich so nicht sagen...
Ich habe dafür eine IInterfacedList<IMyObject> selbst geschrieben basierend auf der TList<IMyObject>.

Zitat:

Zitat von haentschman (Beitrag 1252021)
...die Frage ist doch, was passiert mit dem Inhalt des Interfaces bzw. der Liste mit den Interfaces beim Entladen der DLL? Bleibt das erhalten?

Das ist ja schon beantwortet, daher nur noch ein Hinweis:
Eine Möglichkeit wäre ein Factory-Interface, das du aus deinem Programm mitgibst und die Objekte erstellt. Die DLL muss diese dann nur über die Interfaces füllen und in die Liste hängen.

haentschman 14. Mär 2014 15:38

AW: TObject, DLL, DLL Freigabe
 
Gut...
Dann ist es klar. Die DLL hat dann den gleichen Lebenszyklus zu haben wie die Klasse welche die Objekte / Interfaces / Listen hält. Das bedeutet ein klein wenig Umbau. :(

Danke.


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