views:

168

answers:

1

I'm trying to emulate RapidCRC's ability to check crc32 values within filenames on Windows Vista Ultimate 64-bit. However, I seem to be running into some kind of argument limitation.

I wrote a quick Perl script, created a batch file to call it, then placed a shortcut to the batch file in %APPDATA%\Microsoft\Windows\SendTo

This works great when I select about 20 files or less, right-click and "send to" my batch file script. However, nothing happens at all when I select more than that. I suspect there's a character or number of arguments limit somewhere.

Hopefully I'm missing something simple and that the solution or a workaround isn't too painful.

References:

batch file (crc32_inline.bat):

crc32_inline.pl %*

Perl notes:

I'm using (strawberry) perl v5.10.0

I have C:\strawberry\perl\bin in my path, which is where crc32.bat exists.

perl script (crc32_inline.pl):

#!/usr/bin/env perl

use strict;
use warnings;

use Cwd;
use English qw( -no_match_vars );
use File::Basename;

$OUTPUT_AUTOFLUSH = 1;

my $crc32_cmd = 'crc32.bat';
my $failure_report_basename = 'crc32_failures.txt';
my %failures = ();

print "\n";
foreach my $arg (@ARGV) {

  # if the file has a crc, check to see if it matches the calculated
  # crc.
  if (-f $arg and $arg =~ /\[([0-9a-f]{8})\]/i) {
    my $crc = uc $1;
    my $basename = basename($arg);
    print "checking ${basename}... ";
    my $calculated_crc = uc `${crc32_cmd} "${arg}"`;
    chomp($calculated_crc);
    if ($crc eq $calculated_crc) {
      print "passed.\n";
    }
    else {
      print "FAILED (calculated ${calculated_crc})\n";
      my $dirname = dirname($arg);
      $failures{$dirname}{$basename} = $calculated_crc;
    }
  }
}

print "\nReport Summary:\n";
if (scalar keys %failures == 0) {
  print "  All files OK\n";
}
else {
  print sprintf("  %d / %d files failed crc32 validation.\n" .
                "  See %s for details.\n",
                scalar keys %failures,
                scalar @ARGV,
                $failure_report_basename);

  my $failure_report_fullname = $failure_report_basename;
  if (defined -f $ARGV[0]) {
    $failure_report_fullname
      = dirname($ARGV[0]) . '/' . $failure_report_basename;
  }

  $OUTPUT_AUTOFLUSH = 0;
  open my $fh, '>' . $failure_report_fullname or die $!;
  foreach my $dirname (sort keys %failures) {
    print {$fh} $dirname . "\n";
    foreach my $basename (sort keys %{$failures{$dirname}}) {
      print {$fh} sprintf("  crc32(%s) basename(%s)\n",
                          $failures{$dirname}{$basename},
                          $basename);
    }
  }
  close $fh;
  $OUTPUT_AUTOFLUSH = 1;
}

print sprintf("\n%s done! (%d seconds elapsed)\n" .
              "Press enter to exit.\n",
              basename($0),
              time() - $BASETIME);
<STDIN>;
+1  A: 

I will recommend just putting a shortcut to your script in the "Send To" directory instead of doing it via a batch file (which is subject to cmd.exes limits on command line length).

Sinan Ünür
Running the command directly from the Send To menu is also subject to the limit on the command-line length.
Rob Kennedy
I modified the shortcuts to invoke 'perl "<full_path_name_to_script>"' which seems to work.The creation of the batch files was a silly oversight on my part. I had originally created shortcuts to the perl scripts themselves, hoping that the perl file association would make them executable. Unfortunately, I was wrong, and they did not show up in the send-to menu. Wrapping the script in a batch file seemed to work (at least it appeared in the menu), with the notable input limitation. It wasn't until later that I remembered a Windows shortcut is more like a command than a symlink. Thanks!
vlee
Rob Kennedy: `cmd.exe` has a line length limit of around 8190 characters. The maximum length you can pass `CreateProcess` is 32767 characters. Be careful what exactly you ask for: http://blogs.msdn.com/oldnewthing/archive/2003/12/10/56028.aspx
Joey
Johannes, regardless of which "command line" you're talking about, there's still a limit, and the Send To menu is subject to at least one of them. You can't select an arbitrary number of files and "send" them all at once. The invoked command will only receive the first ones, however many fit on the command line. To allow shell operations on an unlimited number of files, you need to make a shell context-menu extension; since it's part of the shell, it gets API access to the file list instead of having to serialize through a one-string command line.
Rob Kennedy