我正在使用Delphi XE5和XE6,通过启动一个需要管理员权限的进程并使用Delphi函数来编写多个目录链接。
FileCreateSymLink( sLinkPath, sTargetPath )
从这一点开始,我的sLinkPath使用会自动被文件系统定向到sTarget路径。这个功能运作良好。有时我还需要查询这样的链接,以了解它是否为链接以及指向何处。为此,我调用Delphi函数。
function FileGetSymLinkTarget(const FileName: string; var TargetName: string): Boolean;
如果我成功地创建了一个指向本地硬盘上另一个文件夹的链接,这很好。但是当我创建一个指向网络位置(例如)的链接时
\\SERVER\Working\scratch\BJF\test
该链接在文件系统级别上运行良好,但Delphi调用FileGetSymLinkTarget返回false和空目标字符串。进入SysUtils.pas会发现调用了"InternalGetFileNameFromSymLink",这又揭示出使用“try”尝试各种调用来获取有意义的目标信息的许多手势。我注意到,在此例程内部,成功的唯一尝试是调用
GetObjectInfoName(Handle)
返回
\Device\Mup\SERVER\Working\scratch\BJF\test
(关闭!)但是,由于前缀的原因,ExpandVolumeName将其转换为空字符串。
因此,我的问题是:
这可能是XE5和XE6中的一个错误吗? 是否有其他方法可以读取链接的目标而不使用SysUtils?
基于Sertac的示例,我创建了一个程序,它返回本地驱动器和网络路径的正确符号链接路径。虽然我现在调用这个程序来代替SysUtils.FileGetSymLinkTarget,但如果返回的目标为空,也许是为了应对我尚未尝试的重定向,可以先调用SysUtils.FileGetSymLinkTarget,然后仅使用我的程序。
function MyFileGetSymLinkTarget( const APathToLink : string; var ATarget : string ) : boolean;
var
LinkHandle: THandle;
TargetName: array [0..OFS_MAXPATHNAME-1] of Char;
begin
ATarget := '';
LinkHandle := CreateFile( PChar(APathToLink), 0, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
Win32Check(LinkHandle <> INVALID_HANDLE_VALUE);
try
Result := GetFinalPathNameByHandle(LinkHandle, TargetName, OFS_MAXPATHNAME, FILE_NAME_NORMALIZED) > 0;
if Result then
begin
ATarget := TargetName;
if Pos( '\\?\UNC\', ATarget ) = 1 then
begin
Delete( ATarget, 1, 8 );
Insert( '\\', ATarget, 1 );
end
else
if Pos( '\\?\', ATarget ) = 1 then
Delete( ATarget, 1, 4 );
end;
finally
CloseHandle(LinkHandle);
end;
end;
ExpandVolumeName()
将对象前缀转换为本地驱动器字母。由于您的目标不是映射到本地驱动器字母而是映射到 UNC 路径,因此它返回一个空字符串。FileGetSymLinkTarget()
使用未记录的ObjectNameInformation
标志调用NTQueryObject()
来获取对象名称,这就是为什么您会得到系统定义的名称。 - Remy LebeauFSCTL_GET_REPARSE_POINT
。我很惊讶RTL竟然采用手动hack而不使用它们。 - Remy Lebeau(Ansi)StartsText()
来代替使用Pos()=1
。当删除\\?\UNC\
时,请单独使用Delete(ATarget, 3, 6)
,而不是分别使用Delete()
和Insert()
。 - Remy Lebeau