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?
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
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.
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.
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
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.