Determine whether a file is in use in Perl on Windows

file, perl, testing, winapi, windows

Solution

If the recording process locks the file, you could attempt to open it in read-write mode and see if it fails with `ERROR_SHARING_VIOLATION` as `GetLastError` (accessed via Perl's `$^E` special variable).

For example:

#! /usr/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 file ..\n" }

die usage unless @ARGV;

foreach my $path (@ARGV) {
  print "$path: ";

  if (open my $fh, "+<", $path) {
    print "available\n";
    close $fh;
  }
  else {
    print $^E == 0x20 ? "in use by another process\n" : "$!\n";
  }
}

Sample output with `Dir100526Lt.pdf` open by the Adobe reader:

C:\Users\Greg\Downloads>check-lock.pl Dir100526Lt.pdf setup.exe
Dir100526Lt.pdf: in use by another process
setup.exe: available

Be aware that any time you first test a condition and then later act based on the result of that test, you're creating a race condition. It seems that the worst this could bite you in your application is in the following unlucky sequence:

- test a video for availability as above

- answer: available!

- in the meantime, a recorder starts up and locks the video

- back in your program, you try to move the video, but it fails with a sharing violation

Problem

I'm writing some Perl which takes TV shows recorded on Windows Media Center and moves/renames/deletes them depending on certain criteria. Since the Perl runs fairly frequently, I'd like to cleanly determine whether or not the file is in use (in other words, the show is in the process of being recorded) so I can avoid doing anything with it. My current method looks at the status of a file (using "stat") and compares it again after 5 seconds, like so: ``` sub file_in_use { my $file = shift; my @before = stat($file); sleep 5; my @after = stat($file); return 0 if ($before ~~ $after); return 1; } ``` It seems to work, but I'm concious that there is probably a better and cleaner way to do this. Can you please advise?

Original source