如何使用 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】:chdirL
以 The filename or extension is too long
失败。 chdirL
和其他人一样,将路径转换为长路径 (\\?\...
),并调用相应的系统调用。这是SetCurrentDirectoryW
代表chdirL
和GetCurrentDirectoryW
代表getcwdL
。
使用\\?\...
形式的路径会扩展某些调用的使用长度限制,但不适用于SetCurrentDirectoryW
和GetCurrentDirectoryW
。它也不会扩展CreateDirectoryW
、CreateDirectoryExW
和RemoveDirectoryW
的限制。这五个即使在使用“长路径”时也保留了经典的长度限制,至少根据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 - 如何从另一个进程非阻塞读取文件句柄?