Delphi FMX 10.3: Issues with obtaining file properties in for multi-platform applications

  

I am seeking to convert some Delphi code to extract file properties from the operating system from a time-honoured implementation for the Windows platform to a new implementation for the Android platform.

I have found that whereas the implementation for the Delphi platform uses the functions FindFirst() and FindNext() without problem and allows the file properties to be obtained from the TSearchRec parameter, this approach doesn’t appear to work for the Android platform.

So I need to find cross-platform functions to obtain file and directory properties. Several properties may be obtained using methods of the System.IOUtils record types TDirectory and TFile. But for some reason, these does not appear to include a method to obtain the size in bytes of a file.

Therefore, I have tried to obtain the file size by creating a TFileStream object for the file given its pathname, and then getting the size from the TFileStream.Size property. This method works when the file is not already open. However if the file is open, then an exception is thrown.

The next question, then, is how to find out whether a file is open before calling TFileStream.Create(). This ought to be straightforward, but I cannot remember how to do it. So I have tried the following global procedure:

procedure FXGetFileSize(FilePathname: string; var FileInUse: Boolean;
var Size: int64);
var
FileStream: TFileStream;
begin
try
FileStream:= TFileStream.Create(FilePathname, fmOpenRead);
try
FileInUse:= False;
Size:= FileStream.Size;
finally
FileStream.Free;
end;
except
on E: Exception do
begin
FileInUse:= True;
Size:= 0;
FileStream.Free;
end;
end;
end;

I call this procedure from within the following method:

procedure TFolder.ReadFX (Pathname: string; Recurse: Boolean);
{Reads details of folder file components in a folder into a TFolder data structure – cross-platform version}
var
Separator: char;
FolderPaths: TStringDynArray;
FilePathNames: TStringDynArray;
i: integer;
FolderPathI: string;
FilePathnameI: string;
SubFolder: TFolder;
SubFileCpt: TFileCpt;
SubFileCptIndex: integer;
FolderCptName: string;
Datetime: TDatetime;
FileInUse: Boolean;
FileSize: int64;
begin
Separator:= TPath.DirectorySeparatorChar;
FCount:= 0;
FCumSize:= 0;
FCumFileCount:= 0;
FCumFolderCount:= 1;
{Extract list of subfolders in directory:}
FolderPaths:= TDirectory.GetDirectories(Pathname);
{Create a TFolder object for each subfolder:}
for i:= 0 to High(FolderPaths) do
begin
FolderPathI:= FolderPaths[i];
if TDirectory.Exists(FolderPathI) then
begin
try
{Create TFolder object for subfolder i:}
SubFolder:= TFolder.Create;
AddFolderCpt(SubFolder, SubFileCptIndex);
{Assign TFolder properties:}
FolderCptName:= StringReplace(FolderPathI, (Pathname + Separator), ”, [rfIgnoreCase]);
Subfolder.Name:= FolderCptName;
{Subfolder.Name:= Path; }
Subfolder.FDateTime:= TDirectory.GetLastWriteTime(FolderPathI);
{Recursively process subfolder:}
if Recurse then
Subfolder.Read(FolderPathI, Recurse);
{Compute aggregate properties:}
FCumSize:= FCumSize + SubFolder.FCumSize;
FCumFileCount:= FCumFileCount + SubFolder.FCumFileCount;
FCumFolderCount:= FCumFolderCount + SubFolder.FCumFolderCount;
except
on E: Exception do
begin
E.Message:= ‘Error in TFolder.ReadFX processing folder “‘
+ FolderPathI + ‘”‘ + #13#10
+ ‘(‘ + E.Message + ‘)’;
end;
end;
end;
end;
{Get list of files in directory:}
FilePathNames:= TDirectory.GetFiles(Pathname);
{Create TFileCpt objects for each child file:}
for i:= 0 to High(FilePathnames) do
begin
FilePathnameI:= FilePathnames[i];
if (FilePathnameI<> ‘.’) and (FilePathnameI<>’..’) then
begin
try
{$ifdef MSWINDOWS}
if not ([TFileAttribute.faHidden, TFileAttribute.faSystem] <=
TFile.GetAttributes(FilePathnameI)) then
{$endif}
begin
FXGetFileSize(FilePathnameI, FileInUse, FileSize); {***}
{Create a TFileCpt object corresponding to FilePathnameI:}
SubFileCpt:= TFileCpt.Create;
AddFolderCpt(SubFileCpt, SubFileCptIndex);
{Assign TFileCpt properties:}
SubFileCpt.FName:= TPath.GetFileName(FilePathnameI);
SubFileCpt.FSize:= FileSize;
SubFileCpt.FDateTime:= TFile.GetLastWriteTime(FilePathnameI);
FCumSize:= FCumSize + FileSize;
FCumFileCount:= FCumFileCount + 1;
end;
except
on E: Exception do
begin
E.Message:= ‘Error in TFolder.ReadFX processing file “‘
+ FilePathnameI + ‘”‘ + #13#10
+ ‘(‘ + E.Message + ‘)’;
end;
end;
end;
end;
end;

Unfortunately, when I call TFolder.ReadFX() for Pathanme= ‘C:\\Users\User XXX’ on Windows 7, a runtime exception is always thrown when an open file presumably opened by the Windows OS is encountered.

In conclusion, can any one help with the following questions:

How to obtain the size of a file without having to open the file stream
How to determine whether or not the file is already/in use
In the case of a folder, how to extract the name of lowest level folder from the folder path, without manually parsing the path.

Comments are closed.