1

My Perl scripts need to work with pathnames that are longer than 260 characters, and I can not turn on the feature in the registry to enable Windows Long Path support.

I included a small Perl test, using the Win32::LongPath module to do this, and found that only a few functions from that module work. No luck with:

Environment:

  • Windows 10 version 10.0.19041 build 19041
  • Strawberry Perl 5.30.3

I can't really find evidence that Win32::LongPath will not work in that environment, except for CPAN saying that the module has only been tested on XP and Windows 8...

⚠ However all of the help for Perl/Windows Long Paths in Windows 10 seems to recommend this module?

Am I using it wrong? I have included the output of the last iteration of the loop in the MRE (Minimal Reproducible Example):

  • The chdirL command never changes directories.
  • The getcwdL command only has 249 characters (513 expected).
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;

Last loop iteration:

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...
Flandraco
  • 991
  • 10
  • 11

1 Answers1

2

chdirL is failing with The filename or extension is too long. chdirL, like the others, converts the path to a long path (\\?\...), and calls the appropriate system call. This is SetCurrentDirectoryW for chdirL and GetCurrentDirectoryW for getcwdL.

Using paths of the form \\?\... extends the use length limit for some calls, but not for SetCurrentDirectoryW and GetCurrentDirectoryW. It also doesn't extend the limit for CreateDirectoryW, CreateDirectoryExW and RemoveDirectoryW. These five retain the classical length limit even when using "long paths", at least according to Maximum Path Length Limitation, which provides a registry setting PLUS a manifest entry you can use to remove the limit on long paths for those calls.

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>

I don't remember if DLLs have their own manifest or not. If they do, the settings could be changed for just the module, and nothing would break. If they don't and perl's manifest needs to be changed, this would affect all uses of GetCurrentDirectoryW in the process, and that could cause problems. (GetCurrentDirectoryW could return an error because the buffer is too small, which could lead to a failure or crash depending on whether error checking is performed.)

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • But the `mkdirL` works (I assume this gets mapped onto `CreateDirectoryW`)? That would explain why `chdirL` (`SetCurrentDirectoryW`) and `getcwdL` (`GetCurrentDirectoryW`) don't work. But I thought that the whole point of the `W` functions in the Windows API was that they actually work for long paths (as opposed to the `A` functions). And that was supposed to work even if you don't change the registry (nor the manifest)... Unfortunately, my scripts need to run on other machines, where I have no guarantee that the registry and manifest are taken care of... – Flandraco Nov 08 '21 at 15:02
  • Re "*But the `mkdirL` works*", Weird. But I didn't understand why it would need backwards compatibility flag. /// Re "*I assume this gets mapped onto CreateDirectoryW*", [yes](https://metacpan.org/release/RBOISVERT/Win32-LongPath-2.1a/source/xs/LongPath.xs#L144) /// Re "*But I thought that the whole point of the W functions in the Windows API was that they actually work for long paths*", No, they have nothing to do with paths at all. The difference is the character encoding used for text: UTF-16le for "W", and the Active Code Page for "A" functions. – ikegami Nov 08 '21 at 15:08
  • Re "*I have no guarantee that the registry and manifest are taken care of*", That may be true for the Registry. But if the manifest is per DLL,, cause you could submit a change to Win32::LongPath. – ikegami Nov 08 '21 at 15:09
  • CreateDirectoryW has supported \\?\ long paths for a very long time, current directory was one of the things left out back then (along with CreateProcess and LoadLibrary?). – Anders Nov 09 '21 at 00:53
  • @Anders GetCurrentDirectoryW` supports `\\?\`. It just doesn't let you give a longer path by default. – ikegami Nov 09 '21 at 00:57