GetFullPathNameW and long Windows file paths
Asked Answered
C

2

9

In the Windows version of my current personal project, I'm looking to support extended length filepaths. As a result, I'm a little confused with how to use the GetFullPathNameW API to resolve the full name of a long filepath.

According to the MSDN (with regards to the lpFileName parameter):

In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\?\" to the path. For more information, see Naming a File.

If I'm understanding this correctly, in order to use an extended length filepath with GetFullPathNameW, I need to specify a path with the \\?\ prefix attached. Since the \\?\ prefix is only valid before volume letters or UNC paths, this would mean that the API is unusable for resolving the full name of a path relative to the current directory.

If that's the case, is there another API I can use to resolve the full name of a filepath like ..\somedir\somefile.txt if the resulting name's length exceeds MAX_PATH? If not, would I be able to combine GetCurrentDirectory with the relative filepath (\\?\C:\my\cwd\..\somedir\somefile.txt) and use it with GetFullPathNameW, or would I need to handle all of the filepath resolution on my own?

Cabrilla answered 26/6, 2016 at 9:14 Comment(8)
It doesn't sound reasonable that a function to find the full path, requires you to supply the full path. So why not try what the docs say. Maybe you'll be pleasantly surprised.Hypogeous
GetCurrentDirectory() is a unixism that's fundamentally MAX_PATH encumbered. The native OS has no notion of relative paths or default directories, you must always supply it with a full path name. You'll have to get rid of it to get ahead.Sculpturesque
@Cheersandhth.-Alf Wasn't really clear if you were suggesting I try \\?\C:\my\cwd\..\somedir\somefile.txt or \\?\..\somedir\somefile.txt, so I tried both. The first correctly resolves to \\?\C:\my\somedir\somefile.txt, (which answers part of my question) while the second incorrectly resolves to \\?\somedir\somefile.txt.Cabrilla
@HansPassant Just so I'm clear: we're talking about GetCurrentDirectory, not _getcwd, right? Assuming that's the case, you're saying that GetCurrentDirectory won't work for a long cwd path even if I use GetCurrentDirectoyW with an adequately sized buffer from a process that was created with CreateProcess's lpCurrentDirectory parameter using the \\?\ prefix? if so, that's a really obnoxious limitation.Cabrilla
Same thing, _getcwd() calls GetCurrentDirectory(). It is only a limitation for console mode programs, a programming model from the 1970s. Microsoft has advised for a very, very long time that Windows programs should use AppData and the built-in known directories like Documents. But sure, that model is very hard to get rid of and the library support in a C or C++ program certainly doesn't help. Which is the ultimate reason why MAX_PATH is so difficult to get rid of, way too many programs have an implicit dependency on it with C-style char[] buffer declarations.Sculpturesque
That's unfortunate, but I appreciate you taking the time to explain. The project I'm working on should still be doable - I'll just have to keep this in mind going forward.Cabrilla
This is an ugly hack, but for Vista+ you could try opening the file with CreateFile then use GetFinalPathNameByHandleW to resolve the path. Unfortunately that does require a file to actually exist, or the creation of a temp file at an arbitrary spot on your drive.Jaclin
GetFullPathName is entirely string-based; it's just doing string manipulation on what you give it + whatever the current directory is. The paragraph about \\?\ is just boilerplate copy & paste - the function doesn't actually care what strings you pass it.Dollhouse
J
12
  1. GetFullPathNameA is limited to MAX_PATH characters, because it converts the ANSI name to a UNICODE name beforehand using a hardcoded MAX_PATH-sized (in chars) UNICODE buffer. If the conversion doesn't fail due to the length restrictions, then GetFullPathNameW (or direct GetFullPathName_U[Ex]) is called and the resulting UNICODE name is converted to ANSI.

  2. GetFullPathNameW is a very thin shell over GetFullPathName_U. It is limited to MAXSHORT (0x7fff) length in WCHARs, independent of the \\?\ file prefix. Even without \\?\, it will be work for long (> MAX_PATH) relative names. However, if the lpFileName parameter does not begin with the \\?\ prefix, the result name in the lpBuffer parameter will not begin with \\?\ either.

  3. if you will be use lpBuffer with functions like CreateFileW - this function internally convert Win32Name to NtName. and result will be depended from nape type (RTL_PATH_TYPE). if the name does not begin with \\?\ prefix, the conversion fails because RtlDosPathNameToRelativeNtPathName_U[_WithStatus] fails (because if the path not begin with \\?\ it will be internally call GetFullPathName_U (same function called by GetFullPathNameW) with nBufferLength hardcoded to MAX_PATH (exactly 2*MAX_PATH in bytes – NTDLL functions use buffer size in bytes, not in WCHARs). If name begin with \\?\ prefix, another case in RtlDosPathNameToRelativeNtPathName_U[_WithStatus] is executed – RtlpWin32NtNameToNtPathName, which replaces \\?\ with \??\ and has no MAX_PATH limitation

So the solution may look like this:

if(ULONG len = GetFullPathNameW(FileName, 0, 0, 0))
{
    PWSTR buf = (PWSTR)_alloca((4 + len) * sizeof(WCHAR));
    buf[0] = L'\\', buf[1] = L'\\',  buf[2] = L'?', buf[3] = L'\\';
    if (len - 1 == GetFullPathName(FileName, len, buf + 4, &c))
    {
        CreateFile(buf, ...);
    }
}

So we need to specify a path with the \\?\ prefix attached, but not before GetFullPathName - after!

For more info, read this - The Definitive Guide on Win32 to NT Path Conversion

Janessajanet answered 26/6, 2016 at 13:20 Comment(3)
Upvoted just for the sheer level of detail, the sharing of knowledge. Though think it's a bit risky to assume that the internal implementation is the same in all Windows versions. Is it?Hypogeous
Win32 to NT Path Conversion will be always. however thin details of this convertation really can be changed from version to version. some win32 paths at all incorrect converted. in general, if use Win32 path - we have MAX_PATH practic limitation. for example we cannot create process from exe , if it absolute path length exceeded MAX_PATH. if need use long path names without any limitations or some special paths - need use NT paths (native) and ntdll APIJanessajanet
Thanks, this was super helpful. Hope you don't mind - I submitted an edit up to clean up the English a bit on the first two points.Cabrilla
V
-1

Just to update with the current state:

Starting in Windows 10, version 1607, MAX_PATH limitations have been removed from common Win32 file and directory functions. However, you must opt-in to the new behavior. To enable the new long path behavior, both of the following conditions must be met: ...

For the rest, please see my answer here: https://mcmap.net/q/1315723/-opening-long-file-names-in-windows-using-fopen-with-c

Valois answered 23/8, 2019 at 11:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.