如何以编程方式检查当前进程在 Windows 上是否支持长路径?

Jak*_*les 5 windows winapi

在 Windows 10 版本 1607 中,进程现在可以使用清单属性选择长路径感知 ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath )

如何以编程方式检查调用进程是否支持长路径?请注意,仅检查操作系统版本或注册表项的值是不够的,因为 Windows 版本 >= 1607 的情况下,长路径在系统范围内被禁用,并且长路径不显示进程。

RbM*_*bMm 2

ntdll(在 win10 1607 中)导出下一个 API BOOLEAN NTAPI RtlAreLongPathsEnabled();- 这样你就可以调用它。TRUE如果 LongPaths 已启用,则返回

这里的代码 spinet - 如果 RtlAreLongPathsEnabled返回 false - STATUS_NAME_TOO_LONG(c0000106) 返回

在此输入图像描述

系统需要将 Win32 路径转换为 ​​NT 路径,然后才能在调用 kernel.txt 的任何文件函数中使用它。这是通过调用RtlDosPathNameTo*NtPathName*. 此函数,如果看到路径超过(~) -仅当函数返回时MAX_PATH调用并继续工作。如果错误 -返回。RtlAreLongPathsEnabled()TRUESTATUS_NAME_TOO_LONG

代码RtlAreLongPathsEnabled很简单 - 第一次调用时 - 它检查注册表(并且仅检查注册表)并保存结果。根本不寻找清单。这里正是函数代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

所以我的结论是——在当前的构建中,长路径行为仅依赖于注册表设置,并且绝对不依赖于应用程序清单,尽管有 MSDN。

对于否决票 - 对我来说很有趣 - 您中的某个人是否构建测试应用程序(有或没有清单)并自己测试它,或者您只能阅读文档?

对于那些觉得很难或懒得自己编写代码的人。您可以使用以下代码进行测试:

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();
}
Run Code Online (Sandbox Code Playgroud)

<ws2:longPathAware>true</ws2:longPathAware>并在清单和LongPathsEnabled==0注册表中对其进行测试。它失败了吗?然后在没有清单的情况下但LongPathsEnabled==1在注册表中对其进行测试。工作过吗?

如果是这样,我在 Windows 10.版本 1607.build 14393.0 上进行测试


在 win10 1709 上实现发生了变化:现在RtlAreLongPathsEnabled非常简单:

在此输入图像描述

BOOLEAN RtlAreLongPathsEnabled()
{
    return NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess;
}
Run Code Online (Sandbox Code Playgroud)

在之前的版本中是:

在此输入图像描述

  • 否决票可以说是因为您的答案看起来像是通过尝试未记录的代码来揭示和谴责邪恶文档的大胆尝试。问题是,这样一来,您不仅看起来不可信,而且也无法证明任何事情,因为您所显示的代码路径并不知道是唯一/相关的。 (3认同)
  • 根据我在 v1709 上的测试,长路径支持和 RtlAreLongPathsEnabled 的结果都取决于应用程序清单**和**注册表。如果在注册表中启用但未在清单中启用(反之亦然),则长路径不起作用并且该函数返回 FALSE。 (3认同)
  • @Lexikos - 是的,你是对的 - 与 1607 年相比,1709 年的实现发生了变化 (3认同)
  • 如果您删除答案的全部内容并将其替换为一个表格,在该表格中,您将获得在程序中使用长路径(成功/失败)来获取以下属性的每种可能组合的结果,这将对每个人都有帮助:类型(本机、.net、通用)、体系结构(x86、x64)、是否存在清单、“&lt;ws2:longPathAware&gt;”设置为“true”或“false”,以及注册表标志设置为“1”或“0” `(总共 36 种组合)。 (2认同)