Как я могу преобразовать собственное (NT) имя пути в имя пути Win32?

Я работаю над сообщением некоторой информации, полученной из собственных системных API. (Я знаю, что это плохо.... но я получаю информацию, которую не могу получить иначе, и у меня мало проблем с необходимостью обновлять мое приложение, если/когда это время придет.)

Собственный API возвращает собственные пути, как видно из ob, т. е. \SystemRoot\System32\Ntoskrnl.exe или \??\C:\Program Files\VMWare Workstation\vstor-ws60.sys.

Я могу заменить общие префиксы, т.е.

std::wstring NtPathToWin32Path( std::wstring ntPath )
{
    if (boost::starts_with(ntPath, L"\\\\?\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 4);
        return ntPath;
    }
    if (boost::starts_with(ntPath, L"\\??\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 4);
    }
    if (boost::starts_with(ntPath, L"\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 1);
    }
    if (boost::istarts_with(ntPath, L"globalroot\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 11);
    }
    if (boost::istarts_with(ntPath, L"systemroot"))
    {
        ntPath.replace(ntPath.begin(), ntPath.begin() + 10, GetWindowsPath());
    }
    if (boost::istarts_with(ntPath, L"windows"))
    {
        ntPath.replace(ntPath.begin(), ntPath.begin() + 7, GetWindowsPath());
    }
    return ntPath;
}

TEST(Win32Path, NtPathDoubleQuestions)
{
    ASSERT_EQ(L"C:\\Example", NtPathToWin32Path(L"\\??\\C:\\Example"));
}

TEST(Win32Path, NtPathUncBegin)
{
    ASSERT_EQ(L"C:\\Example", NtPathToWin32Path(L"\\\\?\\C:\\Example"));
}

TEST(Win32Path, NtPathWindowsStart)
{
    ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\Windows\\Hello\\World"));
}

TEST(Win32Path, NtPathSystemrootStart)
{
    ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\SystemRoot\\Hello\\World"));
}

TEST(Win32Path, NtPathGlobalRootSystemRoot)
{
    ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\globalroot\\SystemRoot\\Hello\\World"));
}

но я был бы сильно удивлен, если бы не было какого-то API, родного или другого, который преобразовывал бы их в имена путей Win32. Существует ли такой API?


person Billy ONeal    schedule 14.12.2010    source источник
comment
Помогает ли функция API оболочки PathCanonicalize? msdn.microsoft.com/en-us/ библиотека/bb773569%28v=vs.85%29.aspx   -  person Praetorian    schedule 15.12.2010
comment
@Praetorian: Нет, PathCanonicalize принимает пути Win32. Я пытаюсь получить путь win32.   -  person Billy ONeal    schedule 15.12.2010
comment
Я не знаю ни одной такой функции, и это не всегда возможно: NT может использовать пути, которые Win32 вообще не может. Удачи в любом случае…   -  person ephemient    schedule 15.12.2010
comment
@Billy: вот что ты можешь попробовать. Сначала используйте NtCreateFile (msdn.microsoft.com/ en-us/library/bb432380%28v=vs.85%29.aspx), чтобы открыть файл, том и т. д. для чтения. Затем используйте возвращенный HANDLE, чтобы получить полный путь, как описано здесь msdn.microsoft.com/en-us/library/aa366789%28v=vs.85%29.aspx   -  person Praetorian    schedule 15.12.2010
comment
@ephemient: Верно, но NT должна где-то это сделать. Ведь NtQueryDirectoryFile возвращает собственные пути, а FindFirstFile возвращает пути Win32...   -  person Billy ONeal    schedule 15.12.2010
comment
Мне не кажется окончательным. Конечно, FindFirstFile может легко создавать пути Win32; вы должны дать ему один для начала, в отличие от ZwQueryDirectoryFile, который вместо этого берет РУЧКУ. Конечно, ваша проблема была бы решена, если бы существовал API для возврата пути Win32 из HANDLE.   -  person ephemient    schedule 15.12.2010
comment
При поиске можно найти Получение имени файла из дескриптора файла в MSDN, но он по-прежнему возвращает путь NT и работает только для объектов, которые могут быть сопоставлены с памятью.   -  person ephemient    schedule 15.12.2010
comment
@Ephemient: Если бы я мог открыть фактический дескриптор, я мог бы просто вызвать GetFileInformationByHandleEx ... Я бы предпочел избежать необходимости фактически избегать открытия дескриптора фактического файла, если это вообще возможно.   -  person Billy ONeal    schedule 15.12.2010
comment
@Billy: не все дескрипторы открытых файлов одинаковы. Если вы открываете для уровня доступа QUERY_INFORMATION (а не GENERIC_READ или GENERIC_WRITE), то вы никоим образом не должны мешать другим программам, использующим файл.   -  person Ben Voigt    schedule 15.12.2010
comment
@Ben: Хотя это правда, для этого мне все равно нужен доступ к файлу, которого у меня может не быть.   -  person Billy ONeal    schedule 15.12.2010
comment
Довольно интересно. В последний раз, когда я программировал для Windows, я почти уверен, что GetFileInformationByHandleEx еще не существовало. Я согласен с тем, что open+query не очень приятный способ сделать это (хотя open+mmap+query еще хуже), но похоже, что для этого нет общедоступного метода.   -  person ephemient    schedule 15.12.2010
comment
@ephemient: Ах, похоже, это было добавлено в Vista. Независимо от того. Можно позвонить NtQueryFileInformation и попросить FILE_NAMES_INFORMATION с дескриптора.   -  person Billy ONeal    schedule 15.12.2010
comment
@Praetorian: Поместите это в ответ, чтобы мы могли проголосовать за него.   -  person Billy ONeal    schedule 15.12.2010
comment
@Billy: С удовольствием, вы даже можете иметь честь поднять мою репутацию более чем на 1K :-)   -  person Praetorian    schedule 15.12.2010
comment
Откуда берется GetWindowsPath()? Почему бы не использовать RtlDosPathNameToNtPathName_U_WithStatus?   -  person Norbert Boros    schedule 21.06.2018
comment
@NorbertBoros Кажется, я не могу найти никакой документации по функции с таким названием.   -  person Billy ONeal    schedule 23.06.2018
comment
@BillyONeal Я знаю ... есть так много недокументированных, но они существуют и используются с XP до 10 :) Я опубликую суть или что-то в этом роде с некоторыми из этих функций и пояснениями.   -  person Norbert Boros    schedule 23.06.2018


Ответы (6)


Мы делаем это в производственном коде. Насколько я знаю, нет API (общедоступного или частного), который бы справился с этим. Мы просто сравниваем строки с несколькими префиксами, и это работает для нас.

По-видимому, в ntdll.dll есть функция с именем RtlNtPathNameToDosPathName() (появившаяся в XP?), но я понятия не имею, что она делает; Я предполагаю, что это больше связано с такими вещами, как \Device\Harddisk0.

Хотя я не уверен, что такая функция действительно нужна. Win32 передает пути (в смысле CreateFile и т.д.) в NT; NT не передает пути к Win32. Таким образом, ntdll.dll действительно не нужно переходить с путей NT на пути Win32. В редких случаях, когда какая-либо функция запроса NT возвращает полный путь, любая функция преобразования может быть внутренней для библиотеки DLL Win32 (например, не экспортироваться). Я даже не знаю, беспокоят ли они их, поскольку такие вещи, как GetModuleFileName(), просто вернут любой путь, который использовался для загрузки изображения. Я думаю, это просто дырявая абстракция.

person Luke    schedule 15.12.2010
comment
Довольно много. Единственные, которые я видел на регулярной основе, это \\??\\ и \\SystemRoot, и даже тогда это довольно редко (обычно с низкоуровневыми процессами, запущенными в начале последовательности загрузки. Также иногда в реестре, обычно связаны с указанными процессами. - person Luke; 15.12.2010
comment
Я возился с недокументированными вещами, и все эти префиксы, похоже, здесь распространены. Я думаю, есть причина, по которой они недокументированы: P - person Billy ONeal; 16.12.2010
comment
Жаль, что Microsoft не предоставляет для этого документированный API. Внутренне Windows преобразует пути NT в пути DOS в течение всего дня, но мы, программисты, не можем. Если вам интересно, как использовать RtlNtPathNameToDosPathName(), посмотрите здесь: forum.sysinternals.com/ - person Elmue; 11.08.2015

Вот что вы могли бы попробовать. Сначала используйте NtCreateFile, чтобы открыть файл , объем и т.п. для чтения. Затем используйте возвращенный HANDLE, чтобы получить полный путь, как описано здесь.

person Praetorian    schedule 15.12.2010
comment
+1 :) Обратите внимание, что, вероятно, можно пропустить шаг сопоставления файлов, вызвав NtQueryFileInformation и запросив информационный класс FileNameInformation. (Хотя я еще не проверял это, и, конечно, это касается недокументированных вещей...) - person Billy ONeal; 15.12.2010
comment
GetFinalPathNameByHandle может помочь очистить это! - person Gbps; 19.02.2018

Проверьте это, чтобы получить канонический путь в Win32. Это может быть полезно для вас:

http://pdh11.blogspot.com/2009/05/pathcanonicalize-versus-what-it-says-on.html

person miked    schedule 14.12.2010
comment
+1 - это интересно, но речь идет о превращении пути Win32 в путь Win32 другого типа. Во-первых, у меня нет пути win32. Функции PathXxx не будут работать с тем, что у меня есть. - person Billy ONeal; 15.12.2010

См. мой ответ на этот вопрос.

Вам нужно сначала получить дескриптор файла по этому пути, а затем получить путь Win32 для дескриптора.

person user541686    schedule 04.06.2011
comment
Это работает, но проблема в том, что вам нужен доступ к местоположению файла, чтобы выполнить его (потому что вам нужно иметь возможность открыть дескриптор) — у меня часто этого нет. - person Billy ONeal; 05.06.2011
comment
@Billy: Все, что вам нужно, это FILE_READ_ATTRIBUTES доступ, который вы почти всегда можете получить. Если вы не можете получить его, попробуйте в родительском каталоге, а затем соедините имя файла в конце. (Вы не можете преобразовать без какого-либо дескриптора, потому что это не всегда однозначное сопоставление.) - person user541686; 05.06.2011
comment
@Mehrdad: должна быть возможность конвертировать без дескриптора. FindFirstFile/FindNextFile/FindClose не открывают дескрипторы. Вы можете использовать их для отображения структуры каталогов, даже если вы не можете прочитать какие-либо атрибуты в файле. Это решение работает, так что +1, но мне оно действительно не помогает :( - person Billy ONeal; 05.06.2011
comment
@Billy: На самом деле, FindFirstFile действительно открывает дескриптор каталога! Что заставляет вас думать, что это не так? :) - person user541686; 05.06.2011
comment
@Mehrdad: Каталог, да. Не файлы внутри каталога. - person Billy ONeal; 05.06.2011
comment
@Billy: Но, как я уже упоминал в своем комментарии, вы не можете просто попробовать мой метод в родительском каталоге и соединить имена файлов в конце? Похоже, это сработает. - person user541686; 05.06.2011
comment
@Mehrdad: Это просто превращается в еще один лабиринт манипуляций со строками. Это не будет работать из коробки, например, с \??\C:\Windows\... Удаление последнего файла приводит к C:\Windows, прикрепление .. обратно приводит к c:\Windows\.. . Я думаю, вы могли бы повторно канонизировать потом... в любом случае, текущая серия манипуляций со строками меня еще не подвела. - person Billy ONeal; 05.06.2011
comment
@Billy: Хм, ладно... если у вас возникнут проблемы с вашим текущим методом, я думаю, попробуйте. :) - person user541686; 05.06.2011
comment
GetFinalPathNameByHandle это лучший способ, безусловно. Вам не нужно знать, является ли цель файлом или каталогом, просто используйте CreateFileW, чтобы получить дескриптор вашего пути, передав 0 (ноль) для dwDesiredAccess (FILE_ACCESS) и FILE_FLAG_BACKUP_SEMANTICS (только). Они сообщают CreateFileW, что на самом деле вы не будете открывать файл, что позволяет обойти проблемы с безопасностью, о которых упоминали некоторые люди. Вы по-прежнему сможете использовать GetFinalPathNameByHandle для разрешения полного минимального пути к цели. Я делаю это много и успешно, чтобы разрешать точки повторной обработки между томами в течение всего дня. - person Glenn Slayden; 29.09.2019

Это немного поздно, но я все равно опубликую свой ответ, так как даже сегодня это очень хороший вопрос!

Я поделюсь одной из моих функций, протестированных и использованных для преобразования пути NT в DOS. В моем случае мне также пришлось преобразовать ANSI в UNICODE, так что это небольшой бонус для вас, чтобы увидеть и понять, как это можно сделать.

Весь этот код можно использовать в пользовательском режиме, поэтому нам нужно сначала подготовить некоторые вещи.

Определения и структуры:

typedef NTSTATUS(WINAPI* pRtlAnsiStringToUnicodeString)(PUNICODE_STRING, PANSI_STRING, BOOL);

typedef struct _RTL_BUFFER {
    PUCHAR    Buffer;
    PUCHAR    StaticBuffer;
    SIZE_T    Size;
    SIZE_T    StaticSize;
    SIZE_T    ReservedForAllocatedSize; // for future doubling
    PVOID     ReservedForIMalloc; // for future pluggable growth
} RTL_BUFFER, * PRTL_BUFFER;

typedef struct _RTL_UNICODE_STRING_BUFFER {
    UNICODE_STRING String;
    RTL_BUFFER     ByteBuffer;
    UCHAR          MinimumStaticBufferForTerminalNul[sizeof(WCHAR)];
} RTL_UNICODE_STRING_BUFFER, * PRTL_UNICODE_STRING_BUFFER;

#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS   (0x00000001)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_UNC         (0x00000002)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_DRIVE       (0x00000003)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_ALREADY_DOS (0x00000004)

typedef NTSTATUS(WINAPI* pRtlNtPathNameToDosPathName)(__in ULONG Flags, __inout PRTL_UNICODE_STRING_BUFFER Path, __out_opt PULONG Disposition, __inout_opt PWSTR* FilePart);

#define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE (0x00000001)
#define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING (0x00000002)
#define RTL_DUPSTR_ADD_NULL                          RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
#define RTL_DUPSTR_ALLOC_NULL                        RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING

typedef NTSTATUS(WINAPI* pRtlDuplicateUnicodeString)(_In_ ULONG Flags, _In_ PUNICODE_STRING StringIn, _Out_ PUNICODE_STRING StringOut);

Импорт функций:

pRtlAnsiStringToUnicodeString MyRtlAnsiStringToUnicodeString;
pRtlNtPathNameToDosPathName MyRtlNtPathNameToDosPathName;
pRtlDuplicateUnicodeString MyRtlDuplicateUnicodeString;

MyRtlAnsiStringToUnicodeString = (pRtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAnsiStringToUnicodeString");
MyRtlNtPathNameToDosPathName = (pRtlNtPathNameToDosPathName)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlNtPathNameToDosPathName");
MyRtlDuplicateUnicodeString = (pRtlDuplicateUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDuplicateUnicodeString");

Вспомогательная функция:

NTSTATUS NtPathNameToDosPathName(PUNICODE_STRING DosPath, PUNICODE_STRING NtPath)
{
    NTSTATUS                    Status;
    ULONG_PTR                   BufferSize;
    PWSTR                       Buffer;
    RTL_UNICODE_STRING_BUFFER   UnicodeBuffer;

    BufferSize = NtPath->MaximumLength + MAX_PATH * sizeof(WCHAR);

    Buffer = (PWSTR)_alloca(BufferSize);

    ZeroMemory(&UnicodeBuffer, sizeof(UnicodeBuffer));

    UnicodeBuffer.String = *NtPath;
    UnicodeBuffer.String.Buffer = Buffer;
    UnicodeBuffer.String.MaximumLength = (USHORT)BufferSize;
    UnicodeBuffer.ByteBuffer.Buffer = (PUCHAR)Buffer;
    UnicodeBuffer.ByteBuffer.Size = BufferSize;

    CopyMemory(Buffer, NtPath->Buffer, NtPath->Length);

    MyRtlNtPathNameToDosPathName(0, &UnicodeBuffer, NULL, NULL);

    return MyRtlDuplicateUnicodeString(RTL_DUPSTR_ADD_NULL, &UnicodeBuffer.String, DosPath);
}

Использование функции:

UNICODE_STRING us;
UNICODE_STRING DosPath;
ANSI_STRING as;

as.Buffer = (char*)malloc(strlen(NT_PATH_FILE_OR_DIR) + 1);
strcpy(as.Buffer, NT_PATH_FILE_OR_DIR);
as.Length = as.MaximumLength = us.MaximumLength = us.Length = strlen(NT_PATH_FILE_OR_DIR);

MyRtlAnsiStringToUnicodeString(&us, &as, TRUE);

NtPathNameToDosPathName(&DosPath, &us);

Как уже упоминалось, в моем случае мне нужно было преобразовать ANSI в UNICODE, и это может не относиться к вашему случаю, поэтому вы можете удалить его.

То же, что и выше, можно использовать для создания пользовательских функций и преобразования путей по мере необходимости.

person Norbert Boros    schedule 24.03.2020

Я написал функцию, которая преобразует различные типы имен устройств NT (имена файлов, COM-порты, сетевые пути и т. д.) в пути DOS.

Есть две функции. Один преобразует дескриптор в путь NT, а другой преобразует этот путь NT в путь DOS.

Посмотрите здесь: Как получить имя, связанное с открытым РУЧКА

// "\Device\HarddiskVolume3"                                (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp"                           (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg"          (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg"                  (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt"            (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat"                           (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB"                   (DVD drive)
// "\Device\Serial1"                                        (real COM port)
// "\Device\USBSER000"                                      (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini"                   (network drive share,  Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini"      (network drive share,  Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd"                                            (internet socket)
// "\Device\Console000F"                                    (unique name for any Console handle)
// "\Device\NamedPipe\Pipename"                             (named pipe)
// "\BaseNamedObjects\Objectname"                           (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt"                (HKEY_CLASSES_ROOT\.txt)
person Elmue    schedule 11.08.2015