¿Cómo puedo obtener la ruta sensible a mayúsculas y minúsculas en Windows?

Necesito saber cuál es el camino real de un camino determinado.

Por ejemplo:

La ruta real es: d: \ src \ File.txt
Y el usuario me da: D: \ src \ file.txt
Necesito como resultado: d: \ src \ File.txt

Puedes usar esta función:

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

Como veterano, siempre utilicé FindFirstFile para este propósito. La traducción .Net es:

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

Esto solo le proporciona la carcasa correcta para la parte de nombre de archivo de la ruta, no para la ruta completa.

El comentario de JeffreyLWhitledge proporciona un enlace a una versión recursiva que puede funcionar (aunque no siempre) para resolver la ruta completa.

La forma de obtener la ruta real de un archivo (esto no funcionará para las carpetas) es seguir estos pasos:

  1. Llame a CreateFileMapping para crear una asignación para el archivo.
  2. Llame a GetMappedFileName para obtener el nombre del archivo.
  3. Use QueryDosDevice para convertirlo en un nombre de ruta de estilo MS-DOS.

Si desea escribir un progtwig más sólido que también funcione con directorios (pero con más molestias y algunas características no documentadas), siga estos pasos:

  1. Obtenga un control del archivo / carpeta con NtOpenFile o NtOpenFile .
  2. Llame a NtQueryObject para obtener el nombre completo de la ruta.
  3. Llame a NtQueryInformationFile con FileNameInformation para obtener la ruta relativa al volumen.
  4. Usando las dos rutas anteriores, obtenga el componente de la ruta que representa el volumen en sí. Por ejemplo, si obtiene \Device\HarddiskVolume1\Hello.txt para la primera ruta y \Hello.txt para la segunda, ahora sabrá que la ruta del volumen es \Device\HarddiskVolume1 .
  5. Utilice códigos de control de E / S Mount Manager o QueryDosDevice mal documentados para convertir la parte de volumen de la ruta completa de estilo NT con la letra de la unidad.

Ahora tienes la ruta real del archivo.

Como la respuesta de Borja no funciona para volúmenes donde 8,3 nombres están deshabilitados, aquí la implementación recursiva que sugiere Tergiver (funciona para archivos y carpetas, así como los archivos y carpetas de recursos compartidos UNC pero no en sus nombres de máquina ni sus nombres compartidos).

Los archivos o carpetas no existentes no son un problema, lo que existe se verifica y se corrige, pero es posible que se encuentre con problemas de redirección de carpetas, por ejemplo, cuando se intenta obtener la ruta correcta de “C: \ WinDoWs \ sYsteM32 \ driVErs \ eTC \ Hosts” Obtendrá “C: \ Windows \ System32 \ drivers \ eTC \ hosts” en una ventana de 64 bits, ya que no hay una carpeta “etc” con “C: \ Windows \ sysWOW64 \ drivers”.

Escenario de prueba:

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

Uso:

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

Código:

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

Aquí hay una solución alternativa, funciona en archivos y directorios. Utiliza GetFinalPathNameByHandle, que solo es compatible con aplicaciones de escritorio en Vista / Server2008 o superior de acuerdo con los documentos.

Tenga en cuenta que resolverá un enlace simbólico si le da uno, que es parte de la búsqueda de la ruta “final”.

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

Solución alternativa

Aquí hay una solución que funcionó para mover archivos entre Windows y un servidor utilizando rutas sensibles a las mayúsculas y minúsculas. GetFileSystemEntries() el árbol de directorios y corrige cada entrada con GetFileSystemEntries() . Si parte de la ruta no es válida (nombre UNC o carpeta), entonces corrige la ruta solo hasta ese punto y luego usa la ruta original para lo que no puede encontrar. De todos modos, con suerte esto ahorrará tiempo a otros cuando se trata del mismo problema.

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

En Windows, las rutas no distinguen entre mayúsculas y minúsculas. Entonces, ambos caminos son igualmente reales.

Si desea obtener algún tipo de ruta con mayúsculas y minúsculas canónicas (es decir, cómo Windows cree que debería estar en mayúscula), puede llamar a FindFirstFile () con la ruta como una máscara, y luego tomar el nombre completo del archivo encontrado. Si la ruta no es válida, entonces no obtendrá un nombre canónico, naturalmente.