如何在Windows上获得区分大小写的path?

我需要知道哪条path是真正的path。

例如:

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

你可以使用这个功能:

 [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样式的路径名称。

如果你想编写一个更强大的程序,也可以使用目录(但更多的痛苦和一些无证的功能),请按照下列步骤操作:

  1. 使用CreateFileNtOpenFile获取文件/文件夹的NtOpenFile
  2. 调用NtQueryObject来获取完整的路径名称。
  3. 使用FileNameInformation调用NtQueryInformationFile以获取相对于卷的路径。
  4. 使用上面的两个路径,获取代表卷本身的路径的组件。 例如,如果第一个路径为\Device\HarddiskVolume1\Hello.txt ,第二个路径为\Hello.txt ,则您现在知道该卷的路径为\Device\HarddiskVolume1
  5. 使用文件较少的Mount Manager I / O控制代码或QueryDosDevice将替换整个NT样式路径的卷部分与驱动器QueryDosDevice进行转换。

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

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

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

测试场景:

  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("coreel32.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上,路径不区分大小写。 所以两条路都是同样真实的。

如果你想得到某种具有规范大小写的路径(即Windows如何认为它应该被大写),你可以调用FindFirstFile()和路径作为掩码,然后取出找到的文件的全名。 如果路径无效,那么你将不会得到一个规范的名字,天生的。