尽管 MSDN 文档是什么say https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath :
您还可以通过以下方式启用每个应用程序的新长路径行为
显现:
在测试中我发现这不是真的。所有测试均在 win10 1607.14393.447(和 14393.0) x64 上完成
当我测试有或没有清单的exe时,我发现清单对CreateFileW
, CreateDirectoryW
,(没有意义测试更多功能 - 为什么我稍后解释)
真的当LongPathsEnabled
在注册表中为 1 - 长路径有效(without \\?\
prefix)。当它为 0 时 - 不起作用,即使longPathAware
true
在清单中。 (代码如下)
接下来需要了解:
内核中的所有文件函数只能与NT-paths
所以在调用内核之前我们需要转换Win32-path
no NT-path
对于路径变换存在一些封闭函数RtlDosPathNameTo*NtPathName*
in ntdll
所有的转变Win32
->NT
在此功能中集中完成的路径,
但不直接进入CreateFileW
, FindFirstFileW
,
GetCompressedFileSizeW
...(其他行为将是极其
不合逻辑)
所以确实足够测试 1,2 函数,但不是文章中的全部(但如果
有人不相信并且想要 - 让我们测试一下:))
- 所有这些
kernel32
(and kernelbase
) api 采取Win32-path
和
将其转换为NT
在这里 - 所以没有任何不同 - 是说GetFileAttributesW
直接从本机 c/c++ 代码或从
托管运行时或应用程序容器 - 结果将是相同的
- 全部依赖仅来自
RtlDosPathNameTo*NtPathName*
内部的
执行
ntdll
现在实施并导出 2 个新 API
BOOLEAN NTAPI RtlIsLongPathAwareProcessByManifest();
BOOLEAN NTAPI RtlAreLongPathsEnabled();
RtlAreLongPathsEnabled
- 仅在注册表中查找(第一次调用时)HKLM\SYSTEM\CurrentControlSet\Control\FileSystem @ LongPathsEnabled
将结果缓存在 static var 中并在以下情况下返回 trueLongPathsEnabled != 0
。这个函数调用自RtlDosPathNameTo*NtPathName*
当Win32路径超过MAX_PATH
并且仅当启用长路径时 - 路径才会被转换,否则STATUS_NAME_TOO_LONG
回
RtlIsLongPathAwareProcessByManifest
- 仅在应用程序清单中查找(通过调用RtlQueryActivationContextApplicationSettings(0, 0, L"http://schemas.microsoft.com/SMI/2016/WindowsSettings", L"longPathAware");
)当第一次调用时,再次缓存结果为本地静态 BOOLEAN。
如果我不跳过某些东西 - 这个函数从未被调用过ntdll
并从kernebase.dll
只从一个地方——GetTempPathW
。所以真的结果RtlIsLongPathAwareProcessByManifest
只能对GetTempPathW
结果。
但当测试这个时 - 我完全认为当前 Windows 版本中的错误这里 - 如果长度为TMP
环境变量(或TEMP
等..但是TMP
先查询)超出MAX_PATH
询问RtlAreLongPathsEnabled
and RtlIsLongPathAwareProcessByManifest
如果两者都返回 true (所以这里AND
逻辑) - 系统尝试了这么长TMP
路径,但其中一个缓冲区大小错误WCHAR
- 结果我们陷入无限循环 - [ *- >RtlAllocateHeap
-> STATUS_BUFFER_TOO_SMALL
-> RtlFreeHeap
-> RtlAllocateHeap
-> *]
有人可以测试并确认或反驳这一点吗?不传递文档链接,而是真正测试.
我用于测试的代码(有或没有正确的清单 - 由RtlIsLongPathAwareProcessByManifest
result)
BOOL CreateFolder(LPCWSTR lpPathName)
{
return CreateDirectoryW(lpPathName, 0) || GetLastError() == ERROR_ALREADY_EXISTS;
}
void LPT()
{
// check manifest and registry
BOOLEAN bByManifest = 0, bLongPathsEnabled = 0;
if (HMODULE hmod = GetModuleHandle(L"ntdll"))
{
BOOLEAN (NTAPI * RtlIsLongPathAwareProcessByManifest)();
BOOLEAN (NTAPI * RtlAreLongPathsEnabled)();
if (*(FARPROC*)&RtlIsLongPathAwareProcessByManifest = GetProcAddress(hmod, "RtlIsLongPathAwareProcessByManifest"))
{
bByManifest = RtlIsLongPathAwareProcessByManifest();
}
if (*(FARPROC*)&RtlAreLongPathsEnabled = GetProcAddress(hmod, "RtlAreLongPathsEnabled"))
{
bLongPathsEnabled = RtlAreLongPathsEnabled();
}
}
WCHAR name[128], path[0x8000], *c;
if (bLongPathsEnabled && bByManifest)
{
// hung test in GetTempPathW
__stosw((PUSHORT)path, 'x', MAX_PATH + 1);
path[MAX_PATH + 1] = 0;
if (SetEnvironmentVariable(L"TMP", path))
{
// hung here in infinite loop by windows error
// ---> buffer allocated on sizeof(WCHAR) less than required,
// | got STATUS_BUFFER_TOO_SMALL
// | -< free buffer
GetTempPathW(RTL_NUMBER_OF(path), path);// never return :)
}
}
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();
}