已经有很多类似的问题被问到了,但是没有一个可用的答案:
文档研究
显然,使用应用程序清单文件可能是可能的。SetCurrentDirectoryW的文档声明:
提示 从Windows 10版本1607开始,对于此函数的unicode版本(SetCurrentDirectoryW),您可以选择退出MAX_PATH限制。有关详细信息,请参见命名文件、路径和命名空间中的“最大路径长度限制”部分。
来自清单的一般文档:
清单是伴随并描述并行程序集或隔离应用程序的XML文件。 ... 应用程序清单 描述 隔离的应用程序。它们用于在运行时管理应用程序应绑定到的共享并行程序集的名称和版本。应用程序清单被复制到与应用程序可执行文件相同的文件夹中,或作为应用程序可执行文件的资源包含在其中。
有关程序集清单的文档 再次指出与应用程序清单的区别:
作为DLL中的资源,程序集可供DLL私人使用。程序集清单不能作为EXE的资源包含在其中。EXE文件可以将 应用程序清单 包含为资源。
应用清单文档列出了程序集和assemblyIdentity元素是必需的:
assembly element需要一个属性:
- manifestVersion
- 必须将manifestVersion属性设置为1.0。
- manifestVersion
assemblyIdentity element需要以下属性:
- type
- 值必须为Win32且全部小写。
- name
- 名称格式应为:Organization.Division.Name。例如,Microsoft.Windows.mysampleApp。
- version
- 指定应用程序或程序集版本。使用四部分版本格式:mmmmm.nnnnn.ooooo.ppppp。每个由句点分隔的部分都可以是0-65535(含)。有关详细信息,请参阅Assembly Versions。
- type
除此之外,所有其他元素和属性似乎都是可选的。
assembly element 的附加要求包括:
它的第一个子元素必须是 noInherit 或 assemblyIdentity 元素。 assembly 元素必须在命名空间 "urn:schemas-microsoft-com:asm.v1" 中。 assembly 的子元素也必须在该命名空间中,通过继承或标记实现。
最后,longPathAware element 是可选的,但有望允许 SetCurrentDirectoryW 使用长路径:
文档中的部分展示了这个XML清单的示例:启用长度超过 MAX_PATH 的长路径。该元素支持 Windows 10 版本 1607 及更高版本。有关更多信息,请参阅this article。
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
...
<asmv3:application>
<asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</asmv3:windowsSettings>
</asmv3:application>
...
</assembly>
它似乎并没有完全遵循程序集元素的规则:
程序集元素必须在命名空间“urn:schemas-microsoft-com:asm.v1”中。 程序集的子元素也必须通过继承或标记在此命名空间中。
测试
测试环境为:
- Windows 10 21H2 x64 19044.1586
- VS2022 17.1.1
- Windows SDK 版本 10.0.20348.0
测试应用程序是一个新的C++控制台应用程序,在其中我对附加清单文件进行了以下更改:
#include <iostream>
#include <string>
#include <windows.h>
int main() {
std::wstring const path = LR"(H:\test\longPaths\manySmallLongPaths\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\)";
std::wstring const path2 = LR"(\\?\)" + path;
if (!SetCurrentDirectoryW(path.c_str())) {
printf("Exe SetCurrentDirectory failed 1 - (%d)\n", GetLastError());
if (!SetCurrentDirectoryW(path2.c_str()))
printf("Exe SetCurrentDirectory failed 2 - (%d)\n", GetLastError());
}
}
尝试将所有内容整合在一起,我认为以下文件可能是一个有效的应用清单文件:
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns:asmv1='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<asmv1:assemblyIdentity type='win32' name='my.test.app' version='1.0.0.0' />
<asmv1:application>
<asmv1:windowsSettings>
<asmv1:longPathAware>true</asmv1:longPathAware>
</asmv1:windowsSettings>
</asmv1:application>
</assembly>
但是编译和启动应用程序会导致以下错误:
应用程序无法启动,因为它的并行配置不正确。请参阅应用事件日志或使用命令行 sxstrace.exe 工具获取更多详细信息。
使用 sxstrace.exe
可以查看:
INFO: Parsing Manifest File C:\test\longPaths.exe.
INFO: Manifest Definition Identity is my.test.app,type="win32",version="1.0.0.0".
ERROR: Line 2: The element ws1:longPathAware appears as a child of element urn:schemas-microsoft-com:asm.v1^windowsSettings which is not supported by this version of Windows.
ERROR: Activation Context generation failed.
也许
这并不完全正确(或者我解释错了)。尝试使用longPathAware元素中的完整示例:
<assembly xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv1:assemblyIdentity type='win32' name='my.test.app' version='1.0.0.0' />
<asmv3:application>
<asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
成功运行应用程序,但没有长路径意识(Windows错误代码206 = ERROR_FILENAME_EXCED_RANGE
):
Exe SetCurrentDirectory failed 1 - 206
Exe SetCurrentDirectory failed 2 - 206
结束
我只能说我不知道还有什么其他测试,或者是否可以将longPathAware element添加到清单中以实现我正在尝试实现的应用程序类型。
也许有另一个api可以更改我的应用程序的当前工作文件夹为长路径,我会满意的,但至少_chdir和std::filesystem::current_path有相同的限制。
解决方法
使用短名称,即8.3别名,可能提供有限的解决方法。
对于我的案例,这通常是不可行的,因为短路径并不需要存在;它们可以在整个系统或卷上进行控制:- 可以使用
fsutil 8dot3name query
查询一般状态。
- 可以使用fsutil behavior query disable8dot3 c:
查询每个卷的设置。附注:
- 清单可以嵌入可执行文件或dll中。 - 当包含它的dll被延迟加载时,它将被忽略。 - 当包含它的可执行文件时,延迟加载的dll不会忽略它。 - 因为清单在项目设置中是“附加项”,所以不需要assemblyIdentity元素。