如何使用 .Net 获取符号链接(或重解析点)的目标?

Posted

技术标签:

【中文标题】如何使用 .Net 获取符号链接(或重解析点)的目标?【英文标题】:How to obtain the target of a symbolic link (or Reparse Point) using .Net? 【发布时间】:2011-01-19 03:12:09 【问题描述】:

在 .NET 中,我想我可以通过调用 System.IO.File.GetAttributes() 并检查 ReparsePoint 位来确定文件是否为符号链接。像这样:

var a = System.IO.File.GetAttributes(fileName);
if ((a & FileAttributes.ReparsePoint) != 0)

    // it's a symlink

在这种情况下,如何获取符号链接的目标?


ps:我知道如何创建符号链接。它需要 P/Invoke:

[Interop.DllImport("kernel32.dll", EntryPoint="CreateSymbolicLinkW", CharSet=Interop.CharSet.Unicode)] 
public static extern int CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); 

【问题讨论】:

【参考方案1】:

根据GetFinalPathNameByHandle 提到的答案,这里是执行此操作的 C# 代码(因为所有其他答案都只是指针):

用法

var path = NativeMethods.GetFinalPathName(@"c:\link");

代码:

public static class NativeMethods

    private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

    private const uint FILE_READ_EA = 0x0008;
    private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(IntPtr hObject);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateFile(
            [MarshalAs(UnmanagedType.LPTStr)] string filename,
            [MarshalAs(UnmanagedType.U4)] uint access,
            [MarshalAs(UnmanagedType.U4)] FileShare share,
            IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
            [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
            [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
            IntPtr templateFile);

    public static string GetFinalPathName(string path)
    
        var h = CreateFile(path, 
            FILE_READ_EA, 
            FileShare.ReadWrite | FileShare.Delete, 
            IntPtr.Zero, 
            FileMode.Open, 
            FILE_FLAG_BACKUP_SEMANTICS,
            IntPtr.Zero);
        if (h == INVALID_HANDLE_VALUE)
            throw new Win32Exception();

        try
        
            var sb = new StringBuilder(1024);
            var res = GetFinalPathNameByHandle(h, sb, 1024, 0);
            if (res == 0)
                throw new Win32Exception();

            return sb.ToString();
        
        finally
        
            CloseHandle(h);
        
    

【讨论】:

我认为(但我没有测试过)但是您可以使用 .NET FileStream 对象然后使用var h = yourStream.SafeFileHandle.DangerousGetHandle() 来简化您的代码,当您关闭流时您还可以释放句柄,这样您不需要在该变量上调用CloseHandle(h)。你甚至可以让函数接受 FileStream 而不是字符串。 @ScottChamberlain - 我没有使用 FileStream 的原因是 a) 我不确定它是否会通过 .NET 中未定义的属性和 b) 我不确定它是否会也适用于目录(CreateFile 确实有效)。另外这应该更快(虽然我没有测量它)。 如果你和我一样,接下来你想知道的是:***.com/questions/31439011/… @datguy 您是否知道以\\?\ 开头的路径实际上是有效且可用的?它也有一些好处(告诉 Windows 在可能的情况下使用 Unicode 扩展,绕过MAX_PATH_LENGTH 限制)无论如何,出于美观原因将其剥离,但它是一个完全有效的响应e【参考方案2】:

您必须使用 DeviceIoControl() 并发送 FSCTL_GET_REPARSE_POINT 控制代码。 P/Invoke 和 API 的使用细节非常详细,但它Googles really well。

【讨论】:

这让我找到了 Powershell 社区扩展 (PSCX) 的源代码,它有很好的代码来处理 ReparsePoints。【参考方案3】:

Open the file 使用CreateFile,然后将句柄传递给GetFinalPathNameByHandle。

【讨论】:

@Cheeso:指向在 Vista 中首次亮相的文件的符号链接,AFAIK。因此,所有基于文件的符号链接功能都将具有相同的限制。 重解析点自 Win2k 以来一直存在;它只是指向在 Vista 中首次亮相的文件的符号链接。 由于某种原因,我在调用此方法时总是遇到访问冲突...不知道为什么 Vista 什么都好。 @Grault - 找不到(也不记得)Raymond Chen 的评论,所以更新了需要打开的 CreateFile 函数的链接

以上是关于如何使用 .Net 获取符号链接(或重解析点)的目标?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GetFinalPathNameByHandle 来解析指向本地目录的符号链接?

如何在运行时解析 dll 中的外部符号,而不是使用 Cygwin 进行链接时

链接器如何解析多重定义的全局符号(强弱符号)------深入理解计算机系统

MFC 静态链接未解析的外部符号

dtrace:如何从文件中获取符号链接目标

无法解析的外部符号