I'm working on reporting some information gleaned from native system APIs. (I know this is bad.... but I'm getting information that I can't get otherwise, and I have little issue with having to update my app if/when that time comes around.)
The native API returns native pathnames, as seen by ob
, i.e. \SystemRoot\System32\Ntoskrnl.exe
, or \??\C:\Program Files\VMWare Workstation\vstor-ws60.sys
.
I can replace common prefixes, i.e.
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"));
}
but I'd be strongly surprised if there's not some API, native or otherwise, which will convert these into Win32 path names. Does such an API exist?
We do this in production code. As far as I know there is no API (public or private) that handles this. We just do some string comparisons with a few prefixes and it works for us.
Apparently there is a function named RtlNtPathNameToDosPathName() in ntdll.dll (introduced with XP?), but I have no idea what it does; I would guess it has more to do with stuff like \Device\Harddisk0, though.
I'm not sure there is really a need for such a function, though. Win32 passes paths (in the sense of CreateFile, etc) to NT; NT doesn't pass paths to Win32. So ntdll.dll doesn't really have a need to go from NT paths to Win32 paths. In the rare case where some NT query function returns a full path, any conversion function could be internal to the Win32 dll (e.g. not exported). I don't even know if they bother, as stuff like GetModuleFileName() will just return whatever path was used to load the image. I guess this is just a leaky abstraction.
Here's something you could try. First use NtCreateFile to open the file, volume etc. for reading. Then use the returned HANDLE to get the full path as described here.
Check this out for getting the canonical pathname in Win32. It may be helpful for you:
http://pdh11.blogspot.com/2009/05/pathcanonicalize-versus-what-it-says-on.html
See my answer to this question.
You'd need to first get a handle to the file at that path, and then get the Win32 path for the handle.
I wrote a function that converts different types of NT device names (filenames, COM ports, network paths, etc.) into a DOS path.
There are two functions. One converts a handle into an NT path and the other one converts this NT path into a DOS path.
Have a look here:
How to get name associated with open HANDLE
// "\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)