I guess, my problem is caused by the concept how UnicodeStrings are implemented, but I cannot solve this problem.
I'm trying to scan a directory tree on disk recursively and build a treeview which should show all files and subfolders. Plus, I want to store additional information for each tree node. The TTreeNode object has only a "Data" property (type Pointer) for this purpose, so I allocate memory manually, store the information and assign the allocated pointer to my data property. Everything seems to work fine unless I include usage of a UnicodeString field within my data record.
So, here is my custom data record definition:
type
TFileInformation = record
AbsoluteFileName: UnicodeString;
FileSize: Int64;
FileAttributes: LongInt;
CreationTime, ModificationTime: TDateTime;
end;
And here is my code for directory recusion:
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;
When I uncomment the line containing .AbsoluteFileName :=
, I get an Access Violation (SIGSEGV-Exception in Unix).
I'm currently using Lazarus in objfpc
mode on Debian Linux, but I guess it's the same with Delphi on Windows. The Treeview.Data property value is stored in the "Details" variable in my example code, self.trvOrigin is my treeview control.
When you allocate the Details
record, the memory is not defined.
AbsoluteFileName
is a managed type and must be properly initialized before use.
You need to clear the memory after the allocation:
FillChar(Details^, SizeOf(TFileInformation), #0);
As an alternative, use New(Details)
in combination with Dispose(Details)
.
They will correctly initialize/finalize the record.
Note: Details
must be a typed pointer, PFileInformation
.