Мне нужно знать, какой из них является реальным путем для данного пути.
Например:
Реальный путь: d:\src\File.txt
И пользователь дал мне: D:\src\file.txt
Мне нужно в результате: d:\src\File.txt
Мне нужно знать, какой из них является реальным путем для данного пути.
Например:
Реальный путь: 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 предоставляет ссылку на рекурсивную версию, которая может работать (хотя и не всегда), чтобы разрешить полный путь.
Способ получения фактического пути к файлу (это не работает для папок) заключается в следующем:
CreateFileMapping
, чтобы создать сопоставление для файла.GetMappedFileName
, чтобы получить имя файла.QueryDosDevice
, чтобы преобразовать его в имя пути в стиле MS-DOS.Если вам хочется написать более надежную программу, которая также работает с каталогами (но с болью и несколькими недокументированными функциями), выполните следующие действия:
CreateFile
или NtOpenFile
.NtQueryObject
, чтобы получить полное имя пути.NtQueryInformationFile
с помощью FileNameInformation
, чтобы получить относительный путь по объему.\Device\HarddiskVolume1\Hello.txt
для первого пути и \Hello.txt
для второго, теперь вы знаете, что путь тома \Device\HarddiskVolume1
.QueryDosDevice
, чтобы конвертировать часть тома полного пути NT-стиля с буквой диска.Теперь у вас есть реальный путь к файлу.
Как ответ Borja не работает для томов, где имена 8.3 отключены, вот рекурсивная реализация, которую предлагает Tergiver (работает для файлов и папок, а также файлы и папки UNC-акций, но не на именах их машин или их доли имена).
Неиспользуемые файлы или папки не являются проблемой, то, что существует, проверено и исправлено, но вы можете столкнуться с проблемами перенаправления папок, например, при попытке получить правильный путь "C:\WinDoWs\sYsteM32\driVErs\eTC\Hosts" вы получите "C:\Windows\System32\drivers\eTC\hosts" на 64-битных окнах, так как нет папки "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 пути не зависят от регистра. Таким образом, оба пути одинаково реальны.
Если вы хотите получить какой-то путь с канонической капитализацией (то есть, как Windows думает, что он должен быть заглавным), вы можете вызвать FindFirstFile() с помощью пути в качестве маски, а затем взять полное имя найденного файла. Если путь недействителен, то вы не получите каноническое имя, natually.