Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   TXmlDocument.Create(nil) (https://www.delphipraxis.net/186085-txmldocument-create-nil.html)

Perlsau 2. Aug 2015 10:00

Delphi-Version: 2009

TXmlDocument.Create(nil)
 
Moin allerseits,

in einer Mainform-Unit verwende ich folgende Methode zum Öffnen einer XML-Datei problemlos:
Delphi-Quellcode:
procedure TFormMain.XMLfile_Open(const Datei: String);
Var
  XmlDok : TXmlDocument;
  Root  : IXmlNode;
  Txt   : String;

begin
  XmlDok := TXmlDocument.Create(self);
  Try
    XmlDok.LoadFromFile(Datei);
    Root := XmlDok.DocumentElement;
    If Root.HasChildNodes Then
    Begin
      Txt := Root.ChildNodes['rootfiles'].ChildNodes['rootfile'].AttributeNodes['full-path'].Text;
      Memo1.Lines.Append(Txt);
    End;
  Finally
    XmlDok.Free;
  End;
end;
Nun möchte ich aber das XML-Handling in eine Klasse auslagern und kann dort natürlich nicht self als Eigentümer bei Erzeugen der TXmlDocument-Instanz angeben, sondern muß mich mit nil begnügen:
Delphi-Quellcode:
  XmlDok := TXmlDocument.Create(nil);
Das führt jedoch zu einem Laufzeitfehler:

Im Projekt XMLTEST.exe ist eine Exception der Klasse EInvalidPointer mit der Meldung 'Ungültige Zeigeroperation' aufgetreten.

Was muß ich also tun, um TXmlDocument in einer eigenständigen Klasse zu verwenden, in der ich beim Erzeugen keinen Owner zuweisen kann?

Eine mögliche Lösung wäre natürlich, die Klasse mit Owner zu erzeugen und diesen dann der TXmlDocument-Instanz als solchen zuzuweisen. Das funktioniert zwar, scheint mir aber intuitiv irgendwie nicht ganz das Richtige zu sein ...

Neutral General 2. Aug 2015 11:48

AW: TXmlDocument.Create(nil)
 
Erstell dir eine Dummy-TComponent-Instanz die du dem constructor übergibst. So funktionierts nicht.

Sir Rufo 2. Aug 2015 12:05

AW: TXmlDocument.Create(nil)
 
Das Problem liegt in der gemischten Verwendung von Interfaces und der Klassen-Instanz.

Lösung:
Delphi-Quellcode:
XmlDok:IXmlDocument;
, dann klappt es auch ohne Owner

Perlsau 2. Aug 2015 14:08

AW: TXmlDocument.Create(nil)
 
Zitat:

Zitat von Neutral General (Beitrag 1310662)
Erstell dir eine Dummy-TComponent-Instanz die du dem constructor übergibst. So funktionierts nicht.

Doch, mit der letztgenannten Lösung funktioniert es tatsächlich: Ich weise dem Create der Klasseninstanz als Owner das aufrufende Formular zu und kann dann ohne Probleme diesen Owner auch der Instanz von TXmlDocument zuweisen.

Zitat:

Zitat von Sir Rufo (Beitrag 1310667)
Das Problem liegt in der gemischten Verwendung von Interfaces und der Klassen-Instanz.

Was meinst du damit genau? Wo verwende ich diese beiden gemischt? Der Fehler in der ersten Variante
Delphi-Quellcode:
XmlDok := TXmlDocument.Create(nil);
wird ja bereits beim Versuch, auf das Root-Element zuzugreifen, ausgelöst. Und
Delphi-Quellcode:
TXmlDocument.DocumentElement
ist doch eine Eigenschaft von
Delphi-Quellcode:
TXmlDocument
, oder etwa nicht?

Zitat:

Zitat von Sir Rufo (Beitrag 1310667)
Lösung:
Delphi-Quellcode:
XmlDok:IXmlDocument;
, dann klappt es auch ohne Owner

Okay, das habe ich versucht. IXmlDocument kann nicht erzeugt werden, denn es verfügt nicht über eine Create-Methode.
Delphi-Quellcode:
procedure TFormMain.XMLfile_Open(const Datei: String);
Var
  XmlDok : IXmlDocument;
  Root  : IXmlNode;
  Txt   : String;

begin
  Try
    XmlDok.LoadFromFile(Datei); // erzeugt Zugriffsverletzung
    Root := XmlDok.DocumentElement;
    If Root.HasChildNodes Then
    Begin
      Txt := Root.ChildNodes['rootfiles'].ChildNodes['rootfile'].AttributeNodes['full-path'].Text;
      Memo1.Lines.Append(Txt);
    End;
  Finally
    XmlDok := nil;
  End;
end;
Der Versuch, ein XML-Dokument zu laden, löst jedoch eine Zugriffsverletzung aus:

Im Projekt XMLTEST.exe ist eine Exception der Klasse EAccessViolation mit der Meldung 'Zugriffsverletzung bei Adresse 0047C70D in Modul 'XMLTEST.exe'. Lesen von Adresse 00000000' aufgetreten.

Sir Rufo 2. Aug 2015 14:13

AW: TXmlDocument.Create(nil)
 
Du sollst auch nur das machen, was ich dir gesagt habe.

Ändere nur deinen Original-Code wie folgt ab:
Delphi-Quellcode:
var
  XmlDok: {TXmlDocument} IXmlDocument;
begin
  // keine Änderung am Original-Code!
  XmlDok := TXmlDocument.Create( nil );
  ...
  // ok, try finally und das Free kann ersatzlos gestrichen werden
end;

Perlsau 2. Aug 2015 14:29

AW: TXmlDocument.Create(nil)
 
Okay, das funktioniert tatsächlich :thumb:

Aber ich versteh's nicht: Ich deklariere eine Variable vom Typ IXmlDocument und erzeuge damit eine Instanz der Klasse TXmlDocument? Was passiert da? Als laienhafte Antwort fällt mir da nur sowas wie "... da muß wohl irgendwie das Interface mit eingebunden werden ..." ein ...

Dann schreibst du noch: " try finally und das Free kann ersatzlos gestrichen werden". Ich habe aber irgendwo in der Online-Hilfe gelesen, wenn man TXmlDocument ohne Owner (nil) erzeugt, muß man diese Instanz am Ende wieder auf nil setzen. Mit Owner würde das der Owner übernehmen. Doch offenbar entstehen auch so keine Speicherlecks.

Sir Rufo 2. Aug 2015 14:46

AW: TXmlDocument.Create(nil)
 
Du erzeugst hier eine reference-counted Interface-Instanz.

Sobald der Referenz-Zähler einmal erhöht wurde und wieder auf 0 geht, wird die Instanz verworfen. Gibst du jetzt den Owner mit an, so wird der Referenz-Zähler auf 1 gesetzt und die Instanz verschwindet mit dem Owner. Hier kannst du gefahrlos mit der normalen Klassen-Referenz arbeiten.

Ohne Owner bleibt der Zähler erst mal auf 0, aber jeder Zugriff auf eine Node, ... arbeitet auch intern mit der Interface-Referenz des Dokuments ... und wenn diese Referenz losgelassen wird ... geht der Referenz-Zähler auf 0 und ... genau räumt dir die Xml-Instanz aus dem Speicher.

Abhilfe schafft also selber den Referenz-Zähler zu erhöhen (ich nehme
Delphi-Quellcode:
IXmlDocument
), somit ist der Zähler immer mindestens 1, bis ich die Referenz auf
Delphi-Quellcode:
nil
setze, oder die Methode verlassen wird und die lokale Variable aus dem Scope geht (dann wird die auch auf
Delphi-Quellcode:
nil
gesetzt).


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