3

我正在使用 Delphi XE5 和 XE6,并通过对提示提升和使用 Delphi 函数的进程进行外壳处理以编程方式创建许多目录链接:

FileCreateSymLink( sLinkPath, sTargetPath )

从现在开始,我对 sLinkPath 的使用被文件系统自动引导到 sTarget 路径。这一切都很好。在其他时候,我还需要询问这样的链接以查看 (a) 它是否是链接以及 (b) 它指向的位置。为此,我调用 Delphi 函数

function FileGetSymLinkTarget(const FileName: string; var TargetName: string): Boolean;

通过成功创建指向本地硬盘上另一个文件夹的链接,这可以正常工作。但是,当我创建指向网络位置的链接时,例如

\\SERVER\Working\scratch\BJF\test

该链接在文件系统级别完美运行,但 Delphi 调用 FileGetSymLinkTarget 返回 false 和空目标字符串。进入 SysUtils.pas 会显示对“InternalGetFileNameFromSymLink”的调用,这反过来会显示很多人挥手“尝试”各种调用以获取合理的目标信息。我注意到在这个例程中,成功的一次尝试是调用

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;
4

1 回答 1

3

以下适用于我的测试设置,它使用GetFinalPathNameByHandle.

var
  LinkHandle: THandle;
  TargetName: array [0..512] of Char;
begin
  LinkHandle := CreateFile('[path to sym link]', 0, FILE_SHARE_READ, nil,
      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
  Win32Check(LinkHandle <> INVALID_HANDLE_VALUE);
  try
    if GetFinalPathNameByHandle(LinkHandle, TargetName, 512,
        FILE_NAME_NORMALIZED) > 0 then
      ShowMessage(TargetName)
    else
      RaiseLastOSError;
  finally
    CloseHandle(LinkHandle);
  end;

end;

目标路径显示为\\?\UNC\Server\Share\Folder\SubFolder\。您可以再次测试最左侧\\?\UNC并根据需要替换它\

您也可以将目标路径VOLUME_NAME_NONE替换FILE_NAME_NORMALIZED\Server\Share\Folder\SubFolder\.

RTL 在它的一个尝试中使用相同的函数VOLUME_NAME_NT作为结果类型,它返回类似 的路径Device\Mup\..,然后尝试将字符串的起始部分与本地逻辑卷之一 ( GetLogicalDriveStrings) 匹配。当没有匹配时,由于路径指向网络驱动器,它会返回一个空字符串,正如您所指出的。


请注意 RTL 源代码中关于跨机器边界的符号链接的注释:

符号链接的访问权限在网络驱动器上是不可预测的。因此,不建议通过网络驱动器创建符号链接。要在 Windows Vista 和 Windows 7 下启用符号链接的远程访问,请使用命令:“fsutil behavior set SymlinkEvaluation R2R:1 R2L:1”

于 2014-05-01T19:59:40.347 回答