在Windows 10的1607版本中,进程可以通过使用清单属性(https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath)选择长路径感知。
我如何以编程方式检查调用进程是否具有长路径感知?请注意,仅检查操作系统版本或注册表键的值是不够的,因为存在Windows版本>= 1607、长路径在系统范围内被禁用并且进程未发出长路径信号的情况。
在Windows 10的1607版本中,进程可以通过使用清单属性(https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath)选择长路径感知。
我如何以编程方式检查调用进程是否具有长路径感知?请注意,仅检查操作系统版本或注册表键的值是不够的,因为存在Windows版本>= 1607、长路径在系统范围内被禁用并且进程未发出长路径信号的情况。
NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess = 1;
TEB的定义可以从winternl.h中复制,IsLongPathAwareProcess位是第4个字节中最重要的位。也就是说,这可以被重写为:
最初的回答:在winternl.h中可以找到TEB的定义。IsLongPathAwareProcess位是第四个字节中最高位。简单来说,可以将其重写为:
((unsigned char*)NtCurrentTeb()->ProcessEnvironmentBlock)[3] |= 0x80;
ntdll(在Win10 1607中)导出下一个API BOOLEAN NTAPI RtlAreLongPathsEnabled();
- 因此您可以调用它。如果启用了长路径,则返回TRUE
这是代码片段 - 如果RtlAreLongPathsEnabled
返回false,则会返回STATUS_NAME_TOO_LONG
(c0000106)
RtlDosPathNameTo*NtPathName*
来完成的。如果这些函数发现路径超过了MAX_PATH
(约为260个字符) - 将调用 RtlAreLongPathsEnabled()
并仅在该函数返回TRUE
时继续工作,在返回false时会返回STATUS_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;
}
因此我的结论是-在当前版本中,长路径的行为仅取决于注册表设置,并且绝对不依赖于应用程序清单,尽管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();
}
使用清单中的<ws2:longPathAware>true</ws2:longPathAware>
和注册表中的LongPathsEnabled==0
进行测试。如果失败了,尝试在没有清单的情况下使用LongPathsEnabled==1
的注册表进行测试。是否成功?
如果是这样,在Windows 10上进行测试。版本1607,构建14393.0。
RtlAreLongPathsEnabled
非常简单。
BOOLEAN RtlAreLongPathsEnabled()
{
return NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess;
}
<ws2:longPathAware>
设置为true
或false
,以及注册表标志设置为1
或0
(共36种组合)。 - GSerg