The standard way to get the name of the target file / directory of symbolic links in Delphi is to use the TFile.getSymLinkTarget() method from System.IOUtils . However, if the link is incorrect (the target file is missing), then the call to this function causes an exception.
It was also not possible to use CreateFile and GetFinalPathNameByHandle to get the desired result.
As a test example, I created the file d:\data.txt , created a link to it mklink sl.txt data.txt , then renamed the file data.txt . Thus the link becomes incorrect.
As an alternative option, you can use the deviceIoControl() function to get the necessary information, which allows you to send commands directly to devices, which may include a file system. Among the management commands for working with directories is the FSCTL_GET_REPARSE_POINT code for getting information about links and mount points. As a result of a call to this command, a REPARSE_DATA_BUFFER structure is REPARSE_DATA_BUFFER in which the necessary data is not REPARSE_DATA_BUFFER in an entirely logical way.
Unfortunately, my delphi skills have been somewhat lost lately when I didn’t work with it, so the code can be somewhat clumsy. I rewrote the structure as follows (it seems to be in JVCL):
type REPARSE_DATA_BUFFER = packed record ReparseTag: Cardinal; ReparseDataLength: Word; Reserved: Word; SubstituteNameOffset: Word; SubstituteNameLength: Word; PrintNameOffset: Word; PrintNameLength: Word; Flags: Cardinal; PathBuffer: array[0..1000] of WideChar; end; TReparseDataBuffer = REPARSE_DATA_BUFFER; PReparseDataBuffer = ^TReparseDataBuffer;
I don’t really understand what size the output buffer needs to be transferred, but the calling code must allocate space for it. Otherwise, an error will be received that the size of the structure is insufficient.
const SL_NAME = 'D:\sl.txt'; var h : THandle; path : string; rdb : TReparseDataBuffer; br : cardinal; res : Longbool; begin ZeroMemory(@rdb, sizeof(rdb));; h := CreateFile(SL_NAME, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT + FILE_FLAG_BACKUP_SEMANTICS, 0); res := DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nil, 0, @rdb, sizeof(rdb), br, nil); SetString(path, pchar(@rdb.PathBuffer[rdb.PrintNameOffset div sizeof(WideChar)]), rdb.PrintNameLength div sizeof(WideChar)); writeln('target:', path); CloseHandle(h); readln; end.
When the file is FILE_FLAG_OPEN_REPARSE_POINT , the FILE_FLAG_OPEN_REPARSE_POINT flag is FILE_FLAG_OPEN_REPARSE_POINT , which returns the descriptor of the link, not the destination file. For links to directories, FILE_FLAT_BACKUP_SEMANTICS required (honestly peeped in TFile.getSymlinkTarget() ).
The TReparseDataBuffer structure specifies the offset and length of the beginning of the destination file name in the buffer in bytes.