如何在Windows上获取区分大小写的路径?

时间:2011-01-21 19:49:11

标签: c# .net filepath

我需要知道哪条是给定路径的真实路径。

例如:

真正的路径是:d:\ src \ File.txt
并且用户给我:D:\ src \ file.txt
结果我需要:d:\ src \ File.txt

8 个答案:

答案 0 :(得分:17)

您可以使用此功能:

[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint GetLongPathName(string ShortPath, StringBuilder sb, int buffer);

[DllImport("kernel32.dll")]
static extern uint GetShortPathName(string longpath, StringBuilder sb, int buffer); 

protected static string GetWindowsPhysicalPath(string path)
{
        StringBuilder builder = new StringBuilder(255);

        // names with long extension can cause the short name to be actually larger than
        // the long name.
        GetShortPathName(path, builder, builder.Capacity);

        path = builder.ToString();

        uint result = GetLongPathName(path, builder, builder.Capacity);

        if (result > 0 && result < builder.Capacity)
        {
            //Success retrieved long file name
            builder[0] = char.ToLower(builder[0]);
            return builder.ToString(0, (int)result);
        }

        if (result > 0)
        {
            //Need more capacity in the buffer
            //specified in the result variable
            builder = new StringBuilder((int)result);
            result = GetLongPathName(path, builder, builder.Capacity);
            builder[0] = char.ToLower(builder[0]);
            return builder.ToString(0, (int)result);
        }

        return null;
}

答案 1 :(得分:8)

作为一个老朋友,我总是为此目的使用FindFirstFile。 .Net翻译是:

Directory.GetFiles(Path.GetDirectoryName(userSuppliedName), Path.GetFileName(userSuppliedName)).FirstOrDefault();

这只能获得路径文件名部分的正确大小,而不是整个路径。

JeffreyLWhitledge的评论提供了一个递归版本的链接,该版本可以解决完整路径(但并非总是如此)。

答案 2 :(得分:3)

获取文件的实际路径(这对文件夹不起作用)的方法是按照以下步骤操作:

  1. 调用CreateFileMapping为文件创建映射。
  2. 致电GetMappedFileName获取文件名称。
  3. 使用QueryDosDevice将其转换为MS-DOS样式的路径名。
  4. 如果您想编写一个更强大的程序,该程序也适用于目录(但更多的痛苦和一些未记录的功能),请按照下列步骤操作:

    1. 使用CreateFileNtOpenFile
    2. 获取文件/文件夹的句柄
    3. 致电NtQueryObject以获取完整路径名称。
    4. 使用NtQueryInformationFile致电FileNameInformation以获取音量相对路径。
    5. 使用上面的两个路径,获取表示卷本身的路径的组件。例如,如果您获得第一条路径\Device\HarddiskVolume1\Hello.txt而第二条路径获得\Hello.txt,则您现在知道该路径的路径为\Device\HarddiskVolume1
    6. 使用记录不良的Mount Manager I / O控制代码或QueryDosDevice转换替换完整NT样式路径的卷部分和驱动器号。
    7. 现在您拥有该文件的真实路径。

答案 3 :(得分:1)

由于Borja的答案不适用于禁用8.3名称的卷,这里是Tergiver建议的递归实现(适用于文件和文件夹,以及UNC共享的文件和文件夹,但不适用于其机器名称及其共享名)。

不存在的文件或文件夹没有问题,存在的内容经过验证和纠正,但是您可能会遇到文件夹重定向问题,例如在尝试获取“C:\ WinDoWs \ sYsteM32 \ drivers \ eTC”的正确路径时\主机“你会在64位Windows上获得”C:\ Windows \ System32 \ drivers \ eTC \ hosts“,因为没有”etc“文件夹包含”C:\ Windows \ sysWOW64 \ drivers“。

测试场景:

        Directory.CreateDirectory(@"C:\Temp\SomeFolder");
        File.WriteAllLines(@"C:\Temp\SomeFolder\MyTextFile.txt", new String[] { "Line1", "Line2" });

用法:

        FileInfo myInfo = new FileInfo(@"C:\TEMP\SOMEfolder\MyTeXtFiLe.TxT");
        String myResult = myInfo.GetFullNameWithCorrectCase(); //Returns "C:\Temp\SomeFolder\MyTextFile.txt"

代码:

public static class FileSystemInfoExt {

    public static String GetFullNameWithCorrectCase(this FileSystemInfo fileOrFolder) {
        //Check whether null to simulate instance method behavior
        if (Object.ReferenceEquals(fileOrFolder, null)) throw new NullReferenceException();
        //Initialize common variables
        String myResult = GetCorrectCaseOfParentFolder(fileOrFolder.FullName);
        return myResult;
    }

    private static String GetCorrectCaseOfParentFolder(String fileOrFolder) {
        String myParentFolder = Path.GetDirectoryName(fileOrFolder);
        String myChildName = Path.GetFileName(fileOrFolder);
        if (Object.ReferenceEquals(myParentFolder, null)) return fileOrFolder.TrimEnd(new char[]{Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
        if (Directory.Exists(myParentFolder)) {
            //myParentFolder = GetLongPathName.Invoke(myFullName);
            String myFileOrFolder = Directory.GetFileSystemEntries(myParentFolder, myChildName).FirstOrDefault();
            if (!Object.ReferenceEquals(myFileOrFolder, null)) {
                myChildName = Path.GetFileName(myFileOrFolder);
            }
        }
        return GetCorrectCaseOfParentFolder(myParentFolder) + Path.DirectorySeparatorChar + myChildName;
    }

}

答案 4 :(得分:1)

替代解决方案

这是一个解决方案,可以让我使用区分大小写的路径在Windows和服务器之间移动文件。它沿着目录树向下走,并使用GetFileSystemEntries()更正每个条目。如果路径的一部分无效(UNC或文件夹名称),则它仅在该点之前更正路径,然后使用原始路径找到它无法找到的路径。无论如何,希望这会在处理同一问题时节省其他时间。

private string GetCaseSensitivePath(string path)
{
    var root = Path.GetPathRoot(path);
    try
    {
        foreach (var name in path.Substring(root.Length).Split(Path.DirectorySeparatorChar))
            root = Directory.GetFileSystemEntries(root, name).First();
    }
    catch (Exception e)
    {
        // Log("Path not found: " + path);
        root += path.Substring(root.Length);
    }
    return root;
}

答案 5 :(得分:0)

这是一个替代解决方案,适用于文件和目录。使用GetFinalPathNameByHandle,仅根据文档支持Vista / Server2008或更高版本上的桌面应用程序。

请注意,如果你给它一个,它将解析一个符号链接,这是找到&#34; final&#34;路径。

// 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);
    }
}

答案 6 :(得分:0)

我试图避免dll导入,所以对我来说最好的方法是使用System.Linq和System.IO.Directory类。

以您为例 实际路径是:d:\ src \ File.txt 用户给我:D:\ src \ file.txt

此代码:

使用System.Linq;

public static class PathUtils
{
    public static string RealPath(string inputPath)
    {
        return Directory.GetFiles(Path.GetDirectoryName(inputPath))
            .FirstOrDefault(p => String.Equals(Path.GetFileName(p), 
                Path.GetFileName(inputPath), StringComparison.OrdinalIgnoreCase));
    }
}

var p = PathUtils.RealPath(@“ D:\ src \ file.txt”);

方法应返回路径“ d:\ src \ File.txt”或“ D:\ src \ File.txt”。

答案 7 :(得分:-3)

在Windows上,路径不区分大小写。所以这两条路径都同样真实。

如果你想获得一些规范大写的路径(即Windows认为它应该大写),你可以用路径作为掩码调用FindFirstFile(),然后获取找到的文件的全名。如果路径无效,那么您将无法获得规范名称。