How can I obtain the case-sensitive path on Windows?

后端 未结 8 623
慢半拍i
慢半拍i 2020-12-01 18:00

I need to know which is the real path of a given path.

For example:

The real path is: d:\\src\\File.txt
And the user give me: D:\\src\\file.txt
I nee

8条回答
  •  南方客
    南方客 (楼主)
    2020-12-01 18:29

    Here's an alternate solution, works on files and directories. Uses GetFinalPathNameByHandle, which is only supported for desktop apps on Vista/Server2008 or above according to docs.

    Note that it will resolve a symlink if you give it one, which is part of finding the "final" path.

    // http://www.pinvoke.net/default.aspx/shell32/GetFinalPathNameByHandle.html
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint GetFinalPathNameByHandle(SafeFileHandle hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);
    private const uint FILE_NAME_NORMALIZED = 0x0;
    
    static string GetFinalPathNameByHandle(SafeFileHandle fileHandle)
    {
        StringBuilder outPath = new StringBuilder(1024);
    
        var size = GetFinalPathNameByHandle(fileHandle, outPath, (uint)outPath.Capacity, FILE_NAME_NORMALIZED);
        if (size == 0 || size > outPath.Capacity)
            throw new Win32Exception(Marshal.GetLastWin32Error());
    
        // may be prefixed with \\?\, which we don't want
        if (outPath[0] == '\\' && outPath[1] == '\\' && outPath[2] == '?' && outPath[3] == '\\')
            return outPath.ToString(4, outPath.Length - 4);
    
        return outPath.ToString();
    }
    
    // http://www.pinvoke.net/default.aspx/kernel32.createfile
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern SafeFileHandle CreateFile(
         [MarshalAs(UnmanagedType.LPTStr)] string filename,
         [MarshalAs(UnmanagedType.U4)] FileAccess access,
         [MarshalAs(UnmanagedType.U4)] FileShare share,
         IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
         [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
         [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
         IntPtr templateFile);
    private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
    
    public static string GetFinalPathName(string dirtyPath)
    {
        // use 0 for access so we can avoid error on our metadata-only query (see dwDesiredAccess docs on CreateFile)
        // use FILE_FLAG_BACKUP_SEMANTICS for attributes so we can operate on directories (see Directories in remarks section for CreateFile docs)
    
        using (var directoryHandle = CreateFile(
            dirtyPath, 0, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open,
            (FileAttributes)FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero))
        {
            if (directoryHandle.IsInvalid)
                throw new Win32Exception(Marshal.GetLastWin32Error());
    
            return GetFinalPathNameByHandle(directoryHandle);
        }
    }
    

提交回复
热议问题