2011-12-06 5 views
6

Sto creando un'istanza di TXMLDocument in fase di runtime, per caricare e analizzare un file XML. È possibile controllare il codice qui sotto:Delphi - TXMLDocument creato in fase di esecuzione genera AV, con componente sul modulo funzionante

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc, StdCtrls; 

type 
    Txml = class(TForm) 
// XMLDocument1: TXMLDocument; 
    Memo1: TMemo; 
    procedure FormCreate(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    xml: Txml; 

implementation 

{$R *.dfm} 

procedure Txml.FormCreate(Sender: TObject); 
var i,j:integer; 
     aNode:IXMLNode; 
     ws:String; 
     XMLDocument1:TXMLDocument; 
begin 
Memo1.Lines.Clear; 
XMLDocument1 := TXMLDocument.Create(nil); 
try 
    XMLDocument1.LoadFromFile('C:\a.xml'); 
    XMLDocument1.Active := true; 
    aNode := XMLDocument1.ChildNodes.First; 
    while aNode<>nil do 
    begin 
    for i := 0 to aNode.ChildNodes.Count-1 do 
    begin 
    if aNode.ChildNodes[i].NodeName = 'Role' then 
     begin 
     Memo1.Lines.Add('Tag - '+aNode.ChildNodes[i].ChildNodes['Tag'].Text); 
     for j := 0 to aNode.ChildNodes[i].ChildNodes.Count-1 do 
     if aNode.ChildNodes[i].ChildNodes[j].HasChildNodes then 
     begin 
      ws := VarToStr(aNode.ChildNodes[i].ChildNodes[j].ChildValues['Tag']); 
      if trim(ws)<>'' then 
      Memo1.Lines.Add(ws); 
      ws := VarToStr(aNode.ChildNodes[i].ChildNodes[j].ChildValues['Value']); 
      if trim(ws)<>'' then 
      Memo1.Lines.Add(ws); 
     end; 
     end; 
    end; 
    aNode := aNode.NextSibling; 
    end; 
    XMLDocument1.Active := false; 
finally 
    FreeAndNil(XMLDocument1); 
end; 
end; 

end. 

Il problema è che questo sta generando un AV. Come probabilmente avete visto, prima che il componente fosse nel form (// XMLDocument1: TXMLDocument;).

Perché quando il componente era nel modulo il codice funzionava, ma quando lo creo in fase di esecuzione genera AV?

LE: soluzione: sulla base delle risposte/commenti e Aiuto Delphi:

XMLDocument1 : IXMLDocument; //not TXMLDocument 

XMLDocument1 := LoadXMLDocument(...); 

FreeAndNil;// must be deleted 
+3

Usa [IXMLDocument] (http://docwiki.embarcadero.com/VCL/en/XMLIntf.IXMLDocument) e come costruttore di [NewXMLDocument] (http://docwiki.embarcadero.com/VCL/en/ XMLDoc.NewXMLDocument). – TLama

+0

Dove accade esattamente AV? – Kromster

+1

Si dovrebbe fare 'ANode: = nil;' dopo il ciclo per rilasciare l'istanza del nodo prima di chiudere e liberare 'XMLDocument1'. –

risposta

14

Da quello che so si dovrebbe utilizzare l'interfaccia IDoc: IXMLDocument; invece.

Da docs:

Quando TXMLDocument viene creato senza un proprietario, si comporta come un oggetto interfacciato . Cioè, quando tutti i riferimenti alla sua interfaccia sono rilasciati , l'istanza di TXMLDocument viene automaticamente liberata. Quando TXMLDocument viene creato con un proprietario, tuttavia, si comporta come qualsiasi altro componente e viene liberato dal proprietario.

In altre parole, quando si crea un'istanza TXMLDocument con nil Proprietario, fanno non chiamata Free() o FreeAndNil() sulla esempio, e deve assegnare l'oggetto a un IXMLDocument variabile così suo riferimento ora attiva il conteggio è gestito correttamente.

+4

In altre parole, quando si crea un'istanza 'TXMLDocument' con un proprietario' nil', * non * chiama 'Free()' o 'FreeAndNil()' sull'istanza, e * deve * assegnare l'oggetto a una variabile 'IXMLDocument' in modo che il suo conteggio dei riferimenti ora attivo sia gestito correttamente. –

2

È necessario fornire un Owner a TXMLDocument durante la creazione in fase di esecuzione.

XMLDocument1 := TXMLDocument.Create(xml); 
+1

sì, in effetti, questo risolve il problema, ma tutto questo codice verrà inserito in una classe speciale, all'interno di un'unità :) +1 – RBA