2

Ich schätze, mein Problem wird durch das Konzept verursacht, wie UnicodeStrings implementiert sind, aber ich kann dieses Problem nicht lösen.Lazarus/Delphi: UnicodeString in selbst zugewiesenen Datensatz-Datentyp verursacht Zugriffsverletzungen

Ich versuche, eine Verzeichnisstruktur auf Platte rekursiv zu scannen und eine Baumansicht zu erstellen, die alle Dateien und Unterordner anzeigen sollte. Außerdem möchte ich zusätzliche Informationen für jeden Baumknoten speichern. Das TTreeNode-Objekt hat zu diesem Zweck nur eine "Data" -Eigenschaft (Typ Pointer), also ordne ich Speicher manuell zu, speichere die Informationen und weise den zugewiesenen Zeiger meiner Dateneigenschaft zu. Alles scheint gut zu funktionieren, es sei denn, ich verwende ein UnicodeString-Feld innerhalb meines Datensatzes.

So, hier ist meine benutzerdefinierte Datensatzdefinition: mein Code für das Verzeichnis recusion

type 
    TFileInformation = record 
    AbsoluteFileName: UnicodeString; 
    FileSize: Int64; 
    FileAttributes: LongInt; 
    CreationTime, ModificationTime: TDateTime; 
    end; 

Und hier ist:

const NO_ERROR = 0; 

procedure ScanDirectory(Folder: UnicodeString; Node: TTreeNode); 

var 
    Details: Pointer; 
    NewNode: TTreeNode; 
    SearchAttributes: LongInt; 
    SearchMask: UnicodeString; 
    SearchRecord: TUnicodeSearchRec; 

begin 
    if (Folder <> '') and (Folder[Length(Folder)] <> DirectorySeparator) then begin 
    Folder += DirectorySeparator; 
    end; 
    SearchMask := Folder + '*'{$IFDEF WINDOWS} + '.*'{$ENDIF}; 
    SearchAttributes := faReadOnly or faHidden or faSysFile or faDirectory or faArchive or faSymLink; 

    if FindFirst(SearchMask, SearchAttributes, SearchRecord) = NO_ERROR then begin 
    repeat 
     if ((SearchRecord.Attr and faDirectory) <> faDirectory) or 
     ((SearchRecord.Name <> '.') and (SearchRecord.Name <> '..')) then begin 
     Details := MemAlloc(SizeOf(TFileInformation)); 
     //TFileInformation(Details^).AbsoluteFileName := Folder + SearchRecord.Name; 
     TFileInformation(Details^).FileAttributes := SearchRecord.Attr; 
     TFileInformation(Details^).FileSize := SearchRecord.Size; 
     TFileInformation(Details^).CreationTime := SearchRecord.Time; 
     //TFileInformation(Details^).ModificationTime := -1; 
     if Node = nil then begin 
      NewNode := self.trvOrigin.Items.AddNode(nil, nil, ansistring(SearchRecord.Name), Details, naAdd); 
     end else begin 
      NewNode := self.trvOrigin.Items.AddNode(nil, Node, ansistring(SearchRecord.Name), Details, naAddChild); 
     end; 

     if (SearchRecord.Attr and (faDirectory or faSymLink)) = faDirectory then begin 
      // only recurse folders which are NOT SymLink: 
      ScanDirectory(Folder + SearchRecord.Name, NewNode); 
     end; 
     end; 
    until FindNext(SearchRecord) <> NO_ERROR; 
    end; 
    FindClose(SearchRecord); 
end; 

Wenn ich die Zeile mit .AbsoluteFileName := Kommentar-, ich Zugriffsverletzung erhalten (SIGSEGV-Ausnahme in Unix). Ich benutze derzeit Lazarus in objfpc Modus auf Debian Linux, aber ich denke, es ist das gleiche mit Delphi unter Windows. Der Treeview.Data-Eigenschaftswert wird in der Variablen "Details" in meinem Beispielcode gespeichert. Self.trvOrigin ist mein Treeview-Steuerelement.

+0

Woher kommt die Funktion 'MemAlloc'? Wie ich mich erinnere, ist es Windows-spezifische Funktion. Kann es nur in der "FreeVision" -Speichereinheit finden (zusätzlich zur "Windows" -Einheit) ... – Abelisto

+0

Ich benutze die Speichereinheit, die in Lazarus enthalten ist. –

+0

Wäre es nicht besser, einfach eine abgeleitete Klasse von TTreeNode zu erstellen, die zusätzliche Felder enthalten würde, die Sie benötigen. Auf diese Weise müssen Sie sich keine Gedanken darüber machen, wie Sie Speicher für zusätzliche Informationen zuweisen und freigeben können. Das einzige Szenario bei der Verwendung der Eigenschaft Data wäre besser, wenn mehrere Baumknoten dieselben zusätzlichen Informationen enthalten oder wenn diese zusätzlichen Informationen separat verwaltet werden. – SilverWarior

Antwort

4

Wenn Sie den Datensatz Details zuordnen, ist der Speicher nicht definiert.

AbsoluteFileName ist ein verwalteter Typ und muss vor der Verwendung ordnungsgemäß initialisiert werden. Sie müssen die Speicher nach der Zuordnung löschen:

FillChar(Details^, SizeOf(TFileInformation), #0); 

Als Alternative mit Dispose(Details)New(Details) in Kombination verwendet werden. Sie werden den Datensatz korrekt initialisieren/finalisieren.

Hinweis: Details muss ein getippter Zeiger sein, PFileInformation.

+0

Vielen Dank, das Problem wurde behoben. Ich dachte, ich initialisiere die Struktur durch eine Zuweisung von jedem Feld - aber Sie haben Recht. –

+0

sonst 'AllocMem' statt' MemAlloc' verwenden; Delphi hat keine 'MemAlloc'-Funktion. – kludg

+0

Warum nicht die neue Funktion verwenden? Das initialisiert den Datensatz ordnungsgemäß und sollte vorzugsweise anstelle von GetMem, AllocMem usw. verwendet werden. Geben Sie den gleichen Datensatz mit Dispose frei und stellen Sie sicher, dass der Zeiger vom richtigen Typ ist. –