如何使用 Perl 中的 Win32::LongPath 模块来操作长路径名?

Posted

技术标签:

【中文标题】如何使用 Perl 中的 Win32::LongPath 模块来操作长路径名?【英文标题】:How can I use the Win32::LongPath module in Perl to manipulate long path names? 【发布时间】:2021-12-20 23:10:00 【问题描述】:

我的 Perl 脚本需要使用超过 260 个字符的路径名,我无法打开注册表中的功能以启用 Windows Long Path 支持。

我包含了一个小的 Perl 测试,使用 Win32::LongPath 模块来执行此操作,并发现该模块中只有少数功能有效。运气不好:

chdirL getcwdL

环境:

Windows 10 版本 10.0.19041 内部版本 19041 草莓 Perl 5.30.3

我真的找不到证据证明Win32::LongPath 在那种环境下不起作用,除了 CPAN 说该模块只在 XP 和 Windows 8 上测试过...

⚠ 然而,Windows 10 中 Perl/Windows Long Paths 的所有帮助似乎都推荐这个模块?

我用错了吗?我在 MRE(最小可重现示例)中包含了循环的最后一次迭代的输出:

chdirL 命令从不更改目录。 getcwdL 命令只有 249 个字符(预期为 513 个)。
package main 1.0;

use strict;
use warnings;
use Carp;

use Readonly;
use File::Spec::Functions;
use Cwd;

use Win32::LongPath;

my $dir       = 'd123456789';
my $file      = 'test.txt';
my $long_path = 'C:\\Temp';
my $long_file;
my $long_root = catdir $long_path, $dir;
my $fh;

# Maximum path length on linux   : 4096
# Maximum path length on Windows :  260
Readonly::Scalar my $MAX_PATH => 512;

chdirL $long_path;

while ( length $long_path < $MAX_PATH ) 
  $long_path = catdir $long_path,  $dir;
  $long_file = catfile $long_path, $file;

  printf "%-5d: %s\n", length $long_path, "Making $long_path...";
  mkdirL $long_path;
  # === Does not change directories ==>
  chdirL $long_path;
  system 'CD';
  # === Truncates path name ==>
  my $curdir = getcwdL;
  printf "%-20s: %s (%d)\n", 'getcwpdL', $curdir, length $curdir;

  printf "%-5d: %s\n", length $long_file, "Making $long_file...";
  openL \$fh, '>', $long_file or die "unable to create file\n";
  print $fh "$long_path\n" or die "unable to print to file\n";
  close $fh                  or die "unable to close file\n";


  last if ( !( testL 'e', $long_path ) );
  last if ( !( testL 'e', $long_file ) );



unlinkL $long_file or warn "unable to delete file\n";

1;

最后一次循环迭代:

513  : Making C:\Temp\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789...
W:\home\_PERL\long_path
getcwpdL            : C:\Temp\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789 (249)
522  : Making C:\Temp\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\d123456789\test.txt...

【问题讨论】:

【参考方案1】:

chdirLThe filename or extension is too long 失败。 chdirL 和其他人一样,将路径转换为长路径 (\\?\...),并调用相应的系统调用。这是SetCurrentDirectoryW 代表chdirLGetCurrentDirectoryW 代表getcwdL

使用\\?\... 形式的路径会扩展某些调用的使用长度限制,但不适用于SetCurrentDirectoryWGetCurrentDirectoryW。它也不会扩展CreateDirectoryWCreateDirectoryExWRemoveDirectoryW 的限制。这五个即使在使用“长路径”时也保留了经典的长度限制,至少根据Maximum Path Length Limitation,它提供了一个注册表设置以及一个清单条目,您可以使用它来消除这些调用的长路径限制。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"LongPathsEnabled"=dword:00000001
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
        <ws2:longPathAware>true</ws2:longPathAware>
    </windowsSettings>
</application>

我不记得 DLL 是否有自己的清单。如果他们这样做,则可以仅更改模块的设置,并且不会有任何问题。如果他们不这样做并且perl 的清单需要更改,这将影响GetCurrentDirectoryW 在此过程中的所有使用,这可能会导致问题。 (GetCurrentDirectoryW 可能会因为缓冲区太小而返回错误,这可能会导致失败或崩溃,具体取决于是否执行错误检查。)

【讨论】:

但是mkdirL 有效(我假设它被映射到CreateDirectoryW)?这可以解释为什么 chdirL (SetCurrentDirectoryW) 和 getcwdL (GetCurrentDirectoryW) 不起作用。但我认为 Windows API 中 W 函数的全部意义在于它们实际上适用于长路径(与 A 函数相反)。即使您不更改注册表(也不更改清单),这也应该可以工作......不幸的是,我的脚本需要在其他机器上运行,我无法保证注册表和清单得到照顾...... . Re "mkdirL 有效",很奇怪。但我不明白为什么它需要向后兼容标志。 /// Re "我假设这被映射到 CreateDirectoryW", yes /// Re "但我认为 Windows API 中 W 函数的全部意义在于它们实际上适用于长路径”,不,它们与路径完全无关。不同之处在于用于文本的字符编码:UTF-16le 用于“W”,而 Active Code Page 用于“A”函数。 Re "我不保证注册表和清单都得到了处理",注册表可能是这样。但如果清单是每个 DLL,因为您可以提交对 Win32::LongPath 的更改。 CreateDirectoryW 支持 \\?\ 长路径很长时间了,当前目录是当时遗漏的东西之一(还有 CreateProcess 和 LoadLibrary?)。 @Anders GetCurrentDirectoryW` 支持`\\?`。默认情况下,它只是不允许您提供更长的路径。

以上是关于如何使用 Perl 中的 Win32::LongPath 模块来操作长路径名?的主要内容,如果未能解决你的问题,请参考以下文章

Perl - Win32 - 如何从另一个进程非阻塞读取文件句柄?

perl win32 api 如何遍历已打开窗口,获得每个窗口的标题内容

如何在 Perl 中从 telnet 屏幕抓取输出?

perl 进程之间的 perl Win32 信号处理

帮助我使用 perl win32 gui 模块

如何在不重新启动的情况下调试 mod_perl2 模块?