On 1/30/06, Linda W <[EMAIL PROTECTED]> wrote:
> demerphq wrote:
> > We dont need this behaviour as we arent going to be dealing with any
> > DLL's that will be started prior to perl starting. Move at boot
> > behaviour is only required for dlls that are loaded at boot. Since the
> > files we care about are only locked because of Perl we only need close
> > perl.
> ---
>         The Windows API "MoveFileEx" is used to move _any_ DLL that is
> locked in memory.  It isn't something primarily used by device drivers,
> but by any "setup" or install program that replaces "DLL"s loaded in
> memory.

Hmm, I dont see anything about moving dll's that are locked in memory
specifically. I do see the MOVEFILE_DELAY_UNTIL_REBOOT stuff tho.
Which is as you've pointed out a useful thing. (Or horrible kludge,
whatever :-)

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/movefileex.asp

D:\ASPerl\811\lib\auto\Cwd>perl -e "use Cwd; $f='Cwd.dll'; unlink $f
or warn qq(unlink $f:$!); rename $f,$f.2 or die qq($f:$!); rename
$f.2,$f or die $f.qq(2:$!);"
unlink Cwd.dll:Permission denied at -e line 1.
D:\ASPerl\811\lib\auto\Cwd>

works out ok as you said, which means that all that we need to do is
to tell EU::Install to try this approach if the normal copy fails.

Unfortunately this wont work for files that are open with different permissions:

D:\ASPerl\811\lib\auto\Cwd>perl -e "open $x,'>X' or die qq(X:$!);
rename 'X','Y' or die qq(X->Y:$!);"
 X->Y:Permission denied at -e line 1.

But it doesnt seem so important, as presumably this is rare.

>         You cannot rely on the DLL being unlocked when perl exits,
> as a system may have another instance of perl that is running
> and using the same Dll.  It's quite possible to have a perl program
> run at system startup -- that would make the DLL "inuse" while the
> system is up.

Well ok, thats true. I hadn't considered that aspect of the problem. I
was more focused on the bare issue of a perl installer not being able
to install XS stuff it used.

> > This needs more investigation. It has to be tested using native win32
> > builds and not cygwin. At the very least Cygwin and native perl use
> > different default permission masks when opening files. Also be wary of
> > cygwin pretending its deleted a file when it has in fact turned on the
> > delete on close flag of the opened file, theres nothing to stop
> > another process from turning it back off afterwards.
> ---
>         "Delete on close"?  I don't believe I'm familiar with such a
> flag in Windows.  Are you referring to the registry
> values "PendingFileRenameOperations" and "PendingFileRenameOperations2"
> that are populated with "move" operations that are to be done upon system
> reboot? These are located under the regkey:
>      "HKLM\System\CurrentControlSet\Control\Session Manager".

No. Im referring to the FILE_FLAG_DELETE_ON_CLOSE flag that you can
specify when calling CreateFile. See

 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/createfile.asp

Note that DeleteFile() is implemented in terms of CreateFile +
FILE_FLAG_DELETE_ON_CLOSE.

>
>         From the article "How to Move Files that are Currently in Use"
> (http://support.microsoft.com/Default.aspx?id=140570):
>
>         "Sometimes Win32 applications need to delete, rename, or move files
>         that are currently being used by the system. One common example is
>         that setup programs need to remove themselves from the user's hard
>         disk when they are finished setting up a software package..."
>
>         The "MoveFileEx" API is supported on both win-nt and win-95 based
> systems.

Heh. The documentation on MoveFileEx specifies that the function isn't
available under Win95. So that is wrong in your experience?

> The exact implementation is OS dependent.  The MS-suggested
> method to replace "inuse" files is to use this API.  The extra bit
> about "renaming the old-inuse" DLL, scheduling it for later deletion,
> and copying the new file into place is only something I've tested on
> NT-based systems.  It's not something guaranteed [sic?] by MS, however.
>         It has the advantage of not requiring a reboot to use the new
> DLL, but the deletion still needs to be scheduled to happen at reboot
> to ensure all programs using the old library are closed.

Yep. when you start considering multiple instance running I think you
are right.

> > I think it sounds interesting, but its occured to me that maybe a
> > better approach is to conceive of a way to prevent the files from
> > being locked in the first place.
> ---
>         This isn't possible even on a unix-based OS.  If a shared
> library (.so) is loaded by some process and used as a demand-paged
> library, the image on disk is used as backing store.  On *nix based
> systems, there is the advantage that the OS can keep open a file
> inode to prevent it's deletion.  Deleting the filesystem
> reference (the file's name on disk) doesn't affect the actual
> inode-based file.  The file-space associated with the inode isn't
> reclaimed by the OS until all processes have closed (no longer need)
> the inode.
>
>         You can generate abnormal behavior on many *nix's if you
> update an executable, "in-place": by copying the new file over the
> old file's inode (ala "cat new.so >old.so").  Deleting the old
> file first, guarantee's you won't overwrite an "inuse" inode.
>
>         In addition to the "movefile" utility, there is also
> a "pendmoves" util that will list files to be deleted (by listing the
> value(s) in the the earlier mentioned registry key(s)).  You can read
> about both utils (and download them) from:
>         http://www.sysinternals.com/Utilities/pendmoves.html

Yep i know it. :-)

>
>         Perhaps I have not understood what you are trying to accomplish.
> I thought you were talking about an error under CPAN where Cwd.pm doesn't
> install correctly on Windows due to the Cwd.dll file being "inuse" when
> the install script tries to copy it into place?

The original issue stems from the fact that MB uses Cwd to do its
business, but MB is the module that is responsible for installing Cwd.
The bigger issue that multiple perl sessions (or other processes)
could also lock the DLL's actually never occured to me. The chicken
and egg problem however was hard to avoid. :-)

Anyway, attached is a patch for ExtUtils::MakeMaker::Install that is
capable of installing Pathtools (ie Cwd) by doing the rename trick
with MoveFileEx for deletion of the old copy. If the rename fails is
arranges to do the whole thing at reboot.

Thanks for the idea Linda.

Yves


--
perl -Mre=debug -e "/just|another|perl|hacker/"
diff -wurd \.cpan\build\ExtUtils-MakeMaker-6.30/lib/ExtUtils/Install.pm ExtUtils-MakeMaker-6.30_patched/lib/ExtUtils/Install.pm
--- \.cpan\build\ExtUtils-MakeMaker-6.30/lib/ExtUtils/Install.pm	2005-03-22 05:30:24.000000000 +0100
+++ ExtUtils-MakeMaker-6.30_patched/lib/ExtUtils/Install.pm	2006-01-30 23:58:27.013626600 +0100
@@ -91,6 +91,7 @@
     use File::Path qw(mkpath);
     use File::Compare qw(compare);

+    my $win32=!$nonono && $^O eq 'MSWin32';
     my(%from_to) = %$from_to;
     my(%pack, $dir, $warn_permissions);
     my($packlist) = ExtUtils::Packlist->new();
@@ -170,7 +171,28 @@
 	    }

 	    if ($diff){
-		if (-f $targetfile){
+    	        if ($win32 && -f $targetfile && !unlink $targetfile) {
+    	            print "Can't remove existing '$targetfile': $!\n";
+    	            require Win32API::File;
+    	            my $tmp= "AAA";
+    	            ++$tmp while -e "$targetfile.$tmp";
+    	            $tmp= "$targetfile.$tmp";
+    	            if ( rename $targetfile, $tmp ) {
+    	                print "However it has been renamed as '$tmp' which ".
+    	                      "will be removed at next reboot.\n";
+    	                Win32API::File::MoveFileEx( $tmp, [],
+    	                    Win32API::File::MOVEFILE_DELAY_UNTIL_REBOOT() )
+    	                    or die "MoveFileEx/Delete '$tmp' failed: $^E\n";
+    	            } else {
+    	                print "Installation cannot be completed until you reboot.\n",
+    	                      "Until then using '$tmp' as the install filename.\n";
+    	                Win32API::File::MoveFileEx( $tmp, $targetfile,
+    	                    Win32API::File::MOVEFILE_REPLACE_EXISTING() |
+    	                    Win32API::File::MOVEFILE_DELAY_UNTIL_REBOOT() )
+    	                    or die "MoveFileEx/Replace '$tmp' failed: $^E\n";
+    	                $targetfile= $tmp;
+    	            }
+    	        } elsif (-f $targetfile) {
 		    forceunlink($targetfile) unless $nonono;
 		} else {
 		    mkpath($targetdir,0,0755) unless $nonono;


Reply via email to