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

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

例如:

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

你可以使用这个function:

 [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; } 

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

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

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

JeffreyLWhitledge的评论提供了一个递归版本的链接,该版本可以解决完整路径(尽管不总是)。

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

  1. 调用CreateFileMapping以创建文件的映射。
  2. 调用GetMappedFileName以获取文件的名称。
  3. 使用QueryDosDevice将其转换为MS-DOS样式的路径名。

如果您想编写一个更强大的程序,该程序也适用于目录(但更多的痛苦和一些未记录的function),请按照下列步骤操作:

  1. 使用CreateFileNtOpenFile获取文件/文件夹的NtOpenFile
  2. 调用NtQueryObject以获取完整路径名。
  3. 使用FileNameInformation调用NtQueryInformationFile以获取卷相对路径。
  4. 使用上面的两个路径,获取表示卷本身的路径的组件。 例如,如果您获得第一个路径的\Device\HarddiskVolume1\Hello.txt和第二个路径的\Hello.txt ,您现在知道卷的路径是\Device\HarddiskVolume1
  5. 使用记录不良的Mount Manager I / O控制代码或QueryDosDevice转换替换完整NT样式路径的卷部分和驱动器号。

现在你有了文件的真实路径。

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

不存在的文件或文件夹没有问题,存在的内容经过validation和更正,但是您可能会遇到文件夹重定向问题,例如在尝试获取“C:\ WinDoWs \ sYsteM32 \ drivers \ eTC \ Hosts”的正确路径时你会在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; } } 

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

请注意,如果您给它一个,它将解析一个符号链接,这是查找“最终”路径的一部分。

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

替代方案

这是一个解决方案,使我可以使用区分大小写的路径在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; } 

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

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