Search code examples
delphifiremonkey

Delphi cross-platform system utilities for system file functions


I am migrating a Delphi 10.3 VCL application to Firemonkey. The units that do not use the GUI compile without any modification except for those which use the Windows-specific system functions mentioned below.

Can anyone point me to cross-platform system file utilities in Delphi/Firemonkey 10.3, or alternative workarounds, to carry out the following actions needed to complete my project:

  • Extract the system icon for a file or application on the basis of its extension or MIME type

  • Execute a system shell command, e.g. to open a file (equivalent to WinAPI ShellExecute)

  • Launch an external program in a child process and wait for its termination (e.g. create a child process using WinAPI CreateProcess and wait using WaitForSingleObject)

  • Get/set a file’s TimeCreated, TimeModified, & TimeAccessed timestamps so that a file copy may be made having the same timestamps as the source file.

I have Googled for these but the hits I have seen only refer to Windows functions. Some hits indicate that Free Pascal provides a TProcess class and ExecuteProcess(), RunCommand(), and OpenDocument() procedures that are described as being cross-platform.

Regarding the timestamps, it is possible to obtain these using the FindFirst and FindNext functions and then reading timestamps in the TSearchRec returned. But I didn't find how to set the timestamps in the FMX version, which I did in the VCL version using relevant Win API functions.

I am not asking for opinions or recommendations about the best out of a number of possible options, but for a factual answer to a specific question about Delphi 10.3's cross-platform support for basic system functions that one would expect to find in any mature GUI OS, such as Windows and Unix-based OS's. I searched for the an answer in the Delphi help system and on Google without success before posting the question on Stackoverflow. The first answer in this thread does just that. That I appreciate.


Solution

  • The four points you are asking are too different for one single question in my view.

    For your last point you find the solution in the unit System.IOUtils.TFile:

    class function GetCreationTime(const Path: string): TDateTime; 
    class procedure SetCreationTime(const Path: string; const CreationTime: TDateTime); 
    class function GetLastAccessTime(const Path: string): TDateTime;
    class procedure SetLastAccessTime(const Path: string; const LastAccessTime: TDateTime);
    class function GetLastWriteTime(const Path: string): TDateTime;
    class procedure SetLastWriteTime(const Path: string; const LastWriteTime: TDateTime); 
    

    For your point 1 to 3, there is yet no solution in Delphi's RTL.

    For your point 2 and 3, I have implemented my own solution that is working for Windows and MacOS:

    uses
      {$IFDEF MSWINDOWS}
      WinApi.ShellApi; 
      {$ENDIF}
      {$IFDEF MACOS}
      Posix.Stdlib;
      {$ENDIF}
    
    class procedure TApiHelpers.OpenFileByOS(const FileName: string);
    begin
      {$IFDEF MSWINDOWS}
      ShellExecute(0, 'open', PChar(FileName), nil, nil, SW_SHOW);
      {$ENDIF}
      {$IFDEF MACOS}
      _system(PAnsiChar(UTF8String('open "' + FileName + '"')));
      {$ENDIF}
    end;
    

    For a final solution, you have to check and handle the result of the API function ShellExecute and System.

    For your first point, I have a solution working on windows only:

      function GetExtImg(const FileName: string; out ExtImg: IExtractImage): boolean;
      var
        Desktop, Target: IShellFolder;
        PIDL: PItemIDList;
        Attr, Eaten: DWORD;
        res: HResult;
      begin
        OleCheck(SHGetDesktopFolder(Desktop));
        OleCheck(Desktop.ParseDisplayName(0, nil, PChar(ExcludeTrailingPathDelimiter(ExtractFilePath(FileName))), Eaten, PIDL, Attr));
        try
          OleCheck(Desktop.BindToObject(PIDL, nil, IShellFolder, Target));
        finally
          fMalloc.Free(PIDL);
        end;
        OleCheck(Target.ParseDisplayName(0, nil, PChar(ExtractFileName(FileName)), Eaten, PIDL, Attr));
        try
          Result := S_OK = Target.GetUIObjectOf(fOwnerForm[0], 1, PIDL, IExtractImage, nil, ExtImg);
        finally
          fMalloc.Free(PIDL);
        end;
      end;