Подтвердить что ты не робот

Как я могу получить чувствительный к регистру путь к Windows?

Мне нужно знать, какой из них является реальным путем для данного пути.

Например:

Реальный путь: d:\src\File.txt
И пользователь дал мне: D:\src\file.txt
Мне нужно в результате: d:\src\File.txt

4b9b3361

Ответ 1

Вы можете использовать эту функцию:

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

Ответ 2

Как старый таймер, для этой цели я всегда использовал FindFirstFile. Перевод .Net:

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

Это дает вам правильный корпус для части имени файла пути, а не цельного пути.

Комментарий JeffreyLWhitledge предоставляет ссылку на рекурсивную версию, которая может работать (хотя и не всегда), чтобы разрешить полный путь.

Ответ 3

Способ получения фактического пути к файлу (это не работает для папок) заключается в следующем:

  • Вызовите CreateFileMapping, чтобы создать сопоставление для файла.
  • Вызовите GetMappedFileName, чтобы получить имя файла.
  • Используйте QueryDosDevice, чтобы преобразовать его в имя пути в стиле MS-DOS.

Если вам хочется написать более надежную программу, которая также работает с каталогами (но с болью и несколькими недокументированными функциями), выполните следующие действия:

  • Получить дескриптор файла/папки с помощью CreateFile или NtOpenFile.
  • Вызовите NtQueryObject, чтобы получить полное имя пути.
  • Вызовите NtQueryInformationFile с помощью FileNameInformation, чтобы получить относительный путь по объему.
  • Используя два пути выше, получите компонент пути, который представляет сам том. Например, если вы получите \Device\HarddiskVolume1\Hello.txt для первого пути и \Hello.txt для второго, теперь вы знаете, что путь тома \Device\HarddiskVolume1.
  • Используйте либо плохо документированные коды управления вводом/выводе управления диспетчером, либо QueryDosDevice, чтобы конвертировать часть тома полного пути NT-стиля с буквой диска.

Теперь у вас есть реальный путь к файлу.

Ответ 4

Как ответ 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;
    }

}

Ответ 5

Здесь альтернативное решение работает с файлами и каталогами. Использует 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);
    }
}

Ответ 6

В Windows пути не зависят от регистра. Таким образом, оба пути одинаково реальны.

Если вы хотите получить какой-то путь с канонической капитализацией (то есть, как Windows думает, что он должен быть заглавным), вы можете вызвать FindFirstFile() с помощью пути в качестве маски, а затем взять полное имя найденного файла. Если путь недействителен, то вы не получите каноническое имя, natually.