7

In Windows 10 version 1607, processes can now opt in to long path awareness using a manifest attribute (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath)

How can I programmatically check if the calling process is long path aware? Note that checking the OS version or the value of the registry key alone is insufficient due to the case where the Windows version is >= 1607, long paths are disabled system wide, and the process is not manifested for long paths.

Jake Petroules
  • 23,472
  • 35
  • 144
  • 225
  • 3
    Reading the documentation between the lines, I think the theory is that you don't need to know. Can you sketch out why you think this might be a problem? – Harry Johnston Dec 04 '16 at 21:37
  • To warn the user that long paths are not available and as a result incorrect behavior may occur. This is a library project, so a manifest resource enabling long paths is not necessarily present. Otherwise it would simply be an OS version check. – Jake Petroules Dec 04 '16 at 22:07
  • but if use paths in format `\\?\\` long paths really available even in XP – RbMm Dec 04 '16 at 22:17
  • 2
    If you're providing a library it's up to the calling process to know if LFNs are allowed or not I would have thought. Anyway it seems like this feature is more of a gimmick than anything else, I doubt any production software can ever rely on it. – Jonathan Potter Dec 04 '16 at 22:23
  • 3
    If you didn't feel the need to warn the users about path length limitations before, I don't see why there is any need to do so now. Even if this specific library is particularly at risk for some reason, the proper solution is surely to use the existing long path support, which fixes the problem for everybody? – Harry Johnston Dec 04 '16 at 22:31
  • 1
    interesting that system really query in registry - `LongPathsEnabled` value and use it (when `RtlAreLongPathsEnabled` called first time) but look like system not use manifest for this. and i create app with appropriate, but LPN still disabled – RbMm Dec 04 '16 at 22:50
  • 2
    There really is no reason to be able to determine this programmatically. If you're writing the process, you already know the answer because you created the manifest. If you're writing a library, you work with whatever paths are passed to you, and you manage your internal path operations in a compatible way (``\\?\``), and if you are passed a long path when the calling app is not manifested, you crash [like you are supposed to](https://blogs.msdn.microsoft.com/oldnewthing/20060927-07/?p=29563/). – GSerg Dec 04 '16 at 23:45
  • 1
    Note that long-path support changes behaviors that are more than what's possible with "\\\\?\\" paths. For one example, the current directory cannot exceed `MAX_PATH - 2` characters (the buffer is static) unless long paths are enabled. Though, even with long paths enabled, a current directory with path length that exceeds `MAX_PATH - 2` can't be set or inherited by `CreateProcess`. It fails as an invalid parameter. – Eryk Sun Jun 28 '19 at 02:36

2 Answers2

2

Despite the documentation says that long path names could be enabled for both Win32 and UWP applications, it is broken for UWP. API KernelBase!BasepIsProcessLongPathAwareByManifest uses SxS API to obtain values from manifest, and this API is not functional for UWP.

The problem could be solved by manually setting proper bit in PEB:

NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess = 1;

definition of TEB could be copied from winternl.h, IsLongPathAwareProcess bit is the most significant bit of 4th byte, i.e this can be rewritten as

((unsigned char*)NtCurrentTeb()->ProcessEnvironmentBlock)[3] |= 0x80;
Moo-Juice
  • 38,257
  • 10
  • 78
  • 128
1

ntdll (in win10 1607) export next API BOOLEAN NTAPI RtlAreLongPathsEnabled(); - so you can call this. it return TRUE if LongPaths Enabled

here code spinet - if RtlAreLongPathsEnabled returned false - STATUS_NAME_TOO_LONG (c0000106) is returned

enter image description here

system need convert Win32 path to NT path before use it in any file functions, which call kernel. this is done by calling RtlDosPathNameTo*NtPathName* . this functions, if see that path exceeds MAX_PATH (~) - called RtlAreLongPathsEnabled() and continue work only if function return TRUE. in case false - STATUS_NAME_TOO_LONG returned.

code of RtlAreLongPathsEnabled is simply - when first time called - it check registry (and only registry) and save result. not looking for manifest at all. here exactly code of function:

BOOLEAN RtlAreLongPathsEnabled()
{
    static BOOLEAN init;
    static BOOLEAN elp;
    if (!init)
    {
        init = true;
        HANDLE hKey;
        KEY_VALUE_PARTIAL_INFORMATION kvpi;
        STATIC_OBJECT_ATTRIBUTES(FileSystemRegKeyName, "\\registry\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\FileSystem");
        if (0 <= ZwOpenKey(&hKey, KEY_READ, &FileSystemRegKeyName))
        {
            STATIC_UNICODE_STRING(LongPathRegKeyValue, "LongPathsEnabled");
            if (0 <= ZwQueryValueKey(hKey, &LongPathRegKeyValue, KeyValuePartialInformation, &kvpi, sizeof(kvpi), &kvpi.TitleIndex) &&
                kvpi.Type == REG_DWORD && kvpi.DataLength == sizeof(DWORD))
            {
                elp = *(DWORD*)kvpi.Data != 0;
            }
            ZwClose(hKey);
        }
    }
    return elp;
}

so my conclusion - in current build long path behavior dependent only from registry settings and absolute not depended from application manifest, despite MSDN.

for down votes - for me simply interesting - are somebody from you build test app (with and without manifest) and test this yourself , or you can only read documentation ?

for those who find it difficult, or too lazy to write the code yourself. you can test with this code:

BOOL CreateFolder(LPCWSTR lpPathName)
{
    return CreateDirectoryW(lpPathName, 0) || GetLastError() == ERROR_ALREADY_EXISTS;
}

void LPT()
{
    WCHAR name[128], path[0x8000], *c;

    if (!SHGetFolderPath(0, CSIDL_PROFILE , 0, 0, path))
    {
        *name = '\\';
        __stosw((PUSHORT)name + 1, '3', RTL_NUMBER_OF(name) - 2);
        name[RTL_NUMBER_OF(name) - 1] = 0;

        c = path + wcslen(path);

        int n = 4;
        do 
        {
            memcpy(c, name, sizeof(name));
            c += RTL_NUMBER_OF(name) - 1;

            if (!CreateFolder(path))
            {
                break;
            }

        } while (--n);

        if (!n)
        {
            wcscpy(c, L"\\1.txt");

            HANDLE hFile = CreateFileW(path, FILE_GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, 0, OPEN_ALWAYS, 0, 0);

            if (hFile != INVALID_HANDLE_VALUE)
            {
                CloseHandle(hFile);
                return ;
            }
        }
    }

    GetLastError();
}

and test it with <ws2:longPathAware>true</ws2:longPathAware> in manifest and LongPathsEnabled==0 in registry. it fail ? and then test it without manifest but with LongPathsEnabled==1 in registry. worked ?

if so i test on windows 10. version 1607. build 14393.0


on win10 1709 implementation changed: now RtlAreLongPathsEnabled is very simply:

enter image description here

BOOLEAN RtlAreLongPathsEnabled()
{
    return NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess;
}

in previous build was:

enter image description here

RbMm
  • 31,280
  • 3
  • 35
  • 56
  • @JakePetroules - you want determinate it from running process or how ? and are you try create app with manifest `longPathAware=true` and check this ? in my test this not help. and research show that manifest really not used, despite MSDN – RbMm Dec 04 '16 at 22:54
  • 1
    Yes I want to determine whether long paths are enabled for the current running process, and I can see the manifest snippet from MSDN is not affecting the output of the RtlAreLongPathsEnabled API call nor does CreateDirectoryW seem to work with a 266-character path I just tested. – Jake Petroules Dec 04 '16 at 22:58
  • @JakePetroules - yes, manifest snippet from MSDN not affecting result of `RtlAreLongPathsEnabled` but i test `CreateFileW` and `CopyFileW` with long paths and both is fail. really win32 api always need convert win32 path to nt-path, and during this conversion is `RtlAreLongPathsEnabled` called if path `long` - i check for `CreateDirectoryW` now.. – RbMm Dec 04 '16 at 23:05
  • Ah, I see: for a process to use long paths it must have the longPathAware manifest entry *AND* the LongPathsEnabled registry key must be set. I thought it was *OR*. The documentation was not clear on this, because it said "You can *also* enable the new long path behavior per app via the manifest" rather than "you *must* enable". – Jake Petroules Dec 04 '16 at 23:09
  • 1
    This third party site (http://winaero.com/blog/how-to-enable-ntfs-long-paths-in-windows-10/) says: "Enabling NTFS long paths will allow manifested Win32 applications and Windows Store applications to access paths beyond the normal 260 char limit per node." which clearly seems to indicate "AND". – Jake Petroules Dec 04 '16 at 23:09
  • @JakePetroules - from MSDN doc i also be sure that here *OR* logic, but in test i view another result. need some more time for more test this – RbMm Dec 04 '16 at 23:12
  • 1
    Yes, it's definitely an *AND* check. My application is manifested for long path awareness. I set the registry key to zero, ran the app, CreateDirectory failed with a 266-character path. I set the registry key to one, ran the app again, and CreateDirectory succeeded. So this function only checks the registry key; seems like the best way to *truly* test this would be to attempt to create a file within %TEMP% with a 255-character (lpMaximumComponentLength) name and see if the call succeeds. – Jake Petroules Dec 04 '16 at 23:19
  • @JakePetroules - windows not looking to manifest at all. only to registry. so even if your app without manifest but in registry long paths enabled - will be worked – RbMm Dec 04 '16 at 23:23
  • @JakePetroules - best way - really call RtlAreLongPathsEnabled – RbMm Dec 04 '16 at 23:24
  • 1
    Right but calling RtlAreLongPathsEnabled won't work in a library because you can't guarantee the calling application is manifested for long path awareness. – Jake Petroules Dec 04 '16 at 23:33
  • @JakePetroules - but even if calling application not manifested - long paths will be work if `LongPathsEnabled==1` in registry ! – RbMm Dec 04 '16 at 23:36
  • No, they will not. [Enabling Win32 long paths will allow *manifested* Win32 applications ... to access paths beyond the normal 260 character limit...](http://stackoverflow.com/a/27694470/11683). – GSerg Dec 04 '16 at 23:41
  • @GSerg - but are you test by self code, not only read docs ? yes or no ? i test and say - windows not looking to manifest! if in registry enabled - we can use long path even without manifest. if disabled in regisry - manifest not helped – RbMm Dec 04 '16 at 23:44
  • When you say "by code", do you mean that you created a manifested application and observed that the value in the manifest does not matter, or do you mean that you disassembled an undocumented Windows function the purpose of which you don't understand? – GSerg Dec 04 '16 at 23:49
  • @GSerg - of course i create app with and without manifest. i test code. but are you tested too ? – RbMm Dec 04 '16 at 23:50
  • @GSerg - yes i mean that i created a manifested application and observed that the value in the manifest does not matter, and how you can say that "i don't understand the purpose of some functions?" from where you know what i understand and what not ? and question for you - are your create manifested app - and test code ? – RbMm Dec 04 '16 at 23:54
  • 3
    The downvotes are arguably because your answer looks like a bold attempt to reveal and denounce the evil documentation by experimenting with undocumented code. The problem is that this way you are not only not going to look credible, but you are also not proving anything because the code path you are showing is not known to be the only/the relevant one. – GSerg Dec 05 '16 at 00:38
  • 2
    It would help everyone much more if you removed the entire contents of your answer and replaced it with a table where you would have a result of using a long path in a program (success/failure) for each possible combination of the following properties: application type (native, .net, universal), architecture (x86, x64), presence of manifest, `` set to `true` or `false`, and the registry flag set to `1` or `0` (36 combinations in total). – GSerg Dec 05 '16 at 00:39
  • It would really help because at least https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath and http://stackoverflow.com/a/27694470/11683 seems to be contradicting with each other, and the evidence is that the answer also depends on the type of the application. – GSerg Dec 05 '16 at 00:40
  • @GSerg - i test 4 combinations - +/- manifest and +/- registry. and long path worked when and only when `LongPathsEnabled` is !0 in registry. – RbMm Dec 05 '16 at 00:43
  • @GSerg - and main and again - are you or somebody check this by self ? only honesty. are this so hard or lazy to check. ? – RbMm Dec 05 '16 at 00:45
  • @GSerg - win32-paths converted to nt-paths in direct in `kernel32.dll`(`kernelbase.dll`) by calling `RtlDosPathNameTo*NtPathName*` functions. result of call depended from `RtlAreLongPathsEnabled` return value on long path. so this can not depended from app type - no absolute any different. sometimes you need to think themselves, not just read the documentation. and my answer to OP question - `RtlAreLongPathsEnabled` return exactly what he ask. how this function work internally this already separate question. in build on which i check - it not use manifest – RbMm Dec 05 '16 at 02:27
  • 3
    According to my testing on v1709, long path support and the result of RtlAreLongPathsEnabled both depend on the application manifest **and** the registry. If enabled in the registry but not enabled in the manifest (or vice versa), long paths do not work and the function returns FALSE. – Lexikos Apr 14 '18 at 09:44
  • 3
    @Lexikos - yes, you are right - implementation was changed in 1709 compare 1607 – RbMm Apr 14 '18 at 11:29