在PyInstaller应用程序中启用Windows长文件路径支持

3
我遇到了一个问题,我将脚本构建成一个使用PyInstaller制作的可执行文件。当作为Python脚本运行时,脚本正常工作,但当作为PyInstaller应用程序运行时,如果遇到路径长度超过260个字符的文件,它就会失败。
我知道这是由于Windows的限制,必须在注册表中和使用启用longPathAware设置的应用程序清单中选择支持更长的路径。顺便说一下,之所以Python本身可以工作,是因为在Python3.6中,开发人员为python.exepythonw.exe启用了此设置
到目前为止,我已经完成了所有这些步骤,确实如果我将以下清单文件与构建的PyInstaller应用程序(使用--onefile模式)放在一起,它可以正常工作:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
    </windowsSettings>
  </application>
</assembly>

然而,为了使应用程序自包含且便于终端用户使用,我试图避免使用外部清单文件,并让PyInstaller将此自定义清单嵌入可执行文件中。据说--manifest选项可以实现这一点 - 至少从PyInstaller 3.5开始,根据changelogPR #3746的说法:
  • (Windows)通过嵌入清单修复单文件模式下的UAC。 (#1729, #3746)
但是当指定了自定义清单文件时,似乎被忽略了,因为在没有外部清单文件的情况下应用程序仍然无法处理长路径,并且在 --onedir 模式下检查捆绑的清单文件时,它看起来只是忽略了自定义的清单文件并创建了这个替代文件:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity language="*" name="Microsoft.Windows.Common-Controls" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" type="win32" version="6.0.0.0"/>
      <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"/>
    </dependentAssembly>
  </dependency>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    </application>
  </compatibility>
</assembly>

我是否做错或理解有误?--manifest选项是否没有实现我所想的功能?这是一个bug吗? 这个答案描述了修改PyInstaller源代码以覆盖嵌入式清单创建的过程。这仍然是必要的吗?
我还发现GitHub上的一个项目似乎也遇到了同样的问题;这个PR的作者说:
“请注意,PyInstaller尚不理解longPathAware设置,并将其从清单中删除。”
我不知道这是否正确,但我认为这确实证明了这是一个bug。
我正在使用PyInstaller 3.6,Python 3.7.2和Windows 10版本1809。

你的程序真的只能在启用了长DOS路径的Windows 10系统中才能正确工作吗?也许你应该考虑规范化使用“\?\”扩展路径,这样可以一直使用长路径回到Windows NT 4(1996年)。 - Eryk Sun
@ErykSun 是的,我已经了解到这个前缀,但它似乎有自己的兼容性问题(而且它的目的不是专门为了启用长路径,而是为了禁用路径字符串的解析)。在我的情况下,这个程序只会被 Windows 10 PC 使用,我们可以使用组策略来实现。然而,我也对此感兴趣,因为还有其他有用的东西可以放在清单中,PyInstaller 应该支持或者至少允许不覆盖它们,例如 dpiAware。至少可以使用外部清单,所以不太繁琐,只是不理想。 - blah238
当应用程序嵌入时,应用清单只是一个PE资源。您可以在后续构建步骤中将其替换为文件 - 至少在PyInstaller得到修复之前。 - Eryk Sun
我考虑过这个方法,但显然在 PyInstaller 应用程序上使用 mt.exe 会破坏它们:https://dev59.com/q2zXa4cB1Zd3GeqPSE-4#14654743 -- 你知道其他的方法吗? - blah238
我在考虑忽略在运行时修改清单的任何选项,而只修改包目录中原始可执行文件中的清单。然而,这仍然会修改包中的文件。 - Eryk Sun
显示剩余2条评论
1个回答

1

你要如何启用这个选项呢?需要在编译后修改应用程序清单吗?如果是的话,那看起来是什么样子的呢? - joshp
我在您的原始帖子中看到了<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>,但我想知道是否可以在编译期间通过命令行参数来指定? - joshp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接