Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Almost cool: removable drive "finder" instead of windows autoplay

by Intrepid (Deacon)
on Jun 02, 2025 at 22:42 UTC ( [id://11165216]=CUFP: print w/replies, xml ) Need Help??

This tool would be cool if it worked flawlessly, and I am shamelessly fishing for help. Rather than Posting in SOPW I'm showing what I've got here so as not to duplicate the same code in two different parts of the Monastery.

Windows autoplay will run an application on a USB drive if set up there, but I don't want to. I want to leave autoplay alone so that it just harmlessly opens an explorer window when the drive is mounted. I have one drive (the "Dragon" drive referenced in the code) with a very cool free image program, the FastStone Image Viewer, portable edition, installed on it. When I had it plugged into a USB expansion doohicky I plugged another USB drive directly into a slot on the computer, and to my shock and amazement Windows "bumped" the already-inserted drive to a different volume letter! So I said to myself, "I haven't done any really specific Win32 perl scripting for a while, let me see if I can write code that will check for a drive with specific characteristics then, in this case, exec a command from the perl code to fire up the image viewer." Without knowing the drive letter.

What's really strange is that the code doesn't seem to iterate through the array @rmvbl unless I use reverse on it! It's the weirdest thing, I swear this code nearly had me tearing out my hair. Thus all the lines marked "# debugging".

To actually run the code you'll have to adapt it to a drive you have on hand and check for a characteristic that Win32::DriveInfo::VolumeInfo() can detect; the vars that receive the retvals of that call are named in a pretty self-explanatory way.

EDIT

I needed a few days to find other ways to mess up and not look at this code, and it came to me. I was returning undef inside the drive iteration loop instead of below it, where it needed to be.

One thing to note: the variable $VolumeName will not have a value for every drive. It's actually a property of the filesystem, not of the entire drive. A small distinction but important. Anyhow, I think that MS Windows-formatted USB drives won't have this property (I could be wrong). This drive, I had formatted on Gnu/Linux.

One final mystery remains unsolved, and maybe a reader knows something pertaining. The call to exec is never supposed to return if successful, according to Perl's documentation, it is just supposed to completely separate itself from the parent process. When I run this code from a terminal commandline , however, it hangs around until I close the spawned child. Why?

Here's the final code, corrected:

#!/usr/bin/env perl # Last modified: Sat Jun 07 2025 01:57:01 PM -04:00 [EDT] use strict; use v5.18; use utf8; use warnings; use Win32::DriveInfo; =head1 NAME DriveFinder =head1 SYNOPSIS To be executed via a desktop shortcut. Command in shortcut: C:\perl\perl\bin\perl.exe "C:/Program Files/DriveFinder" =cut sub survey { my @rmvbl = grep { Win32::DriveInfo::DriveType($_) == 2 ? $_ : undef } Win32::DriveInfo::DrivesInUse(); for my $drv (@rmvbl) { my ( $VolumeName, $VolumeSerialNumber, $MaximumComponentLength, $FileSystemName, @attr ) = Win32::DriveInfo::VolumeInfo($drv); return $drv .":" if $VolumeName eq "FirstFS"; } return undef; } my $DriveVol = &survey; # Maybe chdir to C:\Users\somia\OneDrive\Pictures? - makes no differen +ce. no warnings 'uninitialized'; while ( !exec($DriveVol.'/FS/FSViewer80/FSViewer.exe') ) { say qq[Plug the USB "Dragon" key drive into a USB slot],q[]; sleep 4; $DriveVol = &survey; } __END__ =pod =head1 Drive Types on Win32 0 - the drive type cannot be determined. 1 - the root directory does not exist. 2 - the drive can be removed from the drive (removable). 3 - the disk cannot be removed from the drive (fixed). 4 - the drive is a remote (network) drive. 5 - the drive is a CD-ROM drive. 6 - the drive is a RAM disk. =cut # vim: ft=perl et sw=4 ts=4 :
Jun 02, 2025 at 22:41 UTC

A just machine to make big decisions
Programmed by fellows (and gals) with compassion and vision
We'll be clean when their work is done
We'll be eternally free yes, and eternally young
Donald Fagen —> I.G.Y.
(Slightly modified for inclusiveness)

Replies are listed 'Best First'.
Re: Almost cool: removable drive "finder" instead of windows autoplay
by choroba (Cardinal) on Jun 03, 2025 at 09:27 UTC
    > the code doesn't seem to iterate through the array @rmvbl unless I use reverse on it

    Are you saying the first debugging "Drive: Z:" or whatever is not displayed? Use warn for debugging instead of say as standard error is not buffered.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Almost cool: removable drive "finder" instead of windows autoplay
by harangzsolt33 (Deacon) on Jun 05, 2025 at 15:42 UTC
    Can you try this? I wrote this sub:

    #################################################################### # # This function returns a list of logical drives in Windows. # Each list element starts with drive letter, followed by # drive type, serial number, file system, label, total space # and free space. All items will be separated by a '*' character. # # If one or more letters are provided as an argument, # then data will be returned only about those drives. # Example: # GetDriveInfo('CE') => Returns data about drive C: and E: # If drive E: does not exist, then it will be left # out of the list! # # Usage: STRING = GetDriveInfo([DRIVEs]) # sub GetDriveInfo { my $DRIVES = defined $_[0] ? $_[0] : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; return RunJS("V='$DRIVES';try{FSO=new ActiveXObject('Scripting.FileS +ystemObject');}catch(e){WScript.Quit(0);}A=[];function ToHex(N){N|=0; +var i,X='';for(i=0;i<32;i+=4)X='0123456789ABCDEF'.charAt((N>>i)&15)+X +;return X;}for(i=0;i<V.length;i++){L=V.charAt(i)+':';if(FSO.DriveExis +ts(L)){D=FSO.GetDrive(L);if(D.IsReady){S=ToHex(D.SerialNumber)+'*'+D. +FileSystem+'*'+D.VolumeName+'*'+D.TotalSize+'*'+D.FreeSpace;}A.push(L ++'*'+D.DriveType+'*'+S);}}WScript.StdOut.WriteLine(A.join(\"\\r\\n\") +);"); } # # Usage: INTEGER = RunJS(JSCODE) # sub RunJS { no warnings; $^O =~ m/MSWIN/i && defined $_[0] && length($_[0]) or return 0; mkdir "C:\\TEMP"; my $TEMPFILE = "C:\\TEMP\\JSTMP001.JS"; my $CSCRIPT = "C:\\WINDOWS\\SYSTEM32\\CSCRIPT.EXE"; -f $CSCRIPT or return 0; local *FILE; open(FILE, ">$TEMPFILE") or return 0; binmode FILE; print FILE $_[0]; close FILE; -f $TEMPFILE && -s $TEMPFILE == length($_[0]) or return 0; my $E = system("$CSCRIPT //NOLOGO $TEMPFILE"); unlink $TEMPFILE; return $E + 1; }
      You used Perl to call an external JavaScript interpreter to call a scripting object that is not native to either.

      I can get those same numbers from the underlying Win32 API using a couple of easy Perl modules. It's amazing what you can do in Perl without resorting to external executables calling external interpreters when you are willing to use a module or two.

      # Recreate the JS in actual Perl with Win32-based modules, because the +re's no reason to spawn a javascript interpreter to run a ScriptingOb +ject library to do the underlying win32 api calls that they wrap use warnings; use strict; use Win32API::File (); # comes pre-installed with Strawberry +Perl use Win32::DriveInfo (); # installed using `cpanm Win32::DriveI +nfo` sub GetDriveInfoPL { my $drives = defined $_[0] ? $_[0] : join('', 'A'..'Z'); local $\ = "\n"; my $retval = "\nPerl Gets:\n"; for ( split //, $drives ) { my $drive = $_ . ':'; # GetDriveType is one of many ways to determine whether it's a +n active drive or not, with the benefit that it can distinguish betwe +en types of drives my $type = Win32API::File::GetDriveType($drive); next unless $type > Win32API::File::DRIVE_NO_ROOT_DIR; my $typestr = (qw/DRIVE_UNKNOWN DRIVE_NO_ROOT_DIR DRIVE_REMOVA +BLE DRIVE_FIXED DRIVE_REMOTE DRIVE_CDROM DRIVE_RAMDISK/)[$type]; #print "GetDriveType($drive) = $type => $typestr"; my $fsotype = $type - 1; # the FSO .DriveType is off-by-one fr +om the Win32API::File::GetDriveType() call: <https://learn.microsoft. +com/en-us/office/vba/language/reference/user-interface-help/drivetype +-property> # GetVolumeInformation grabs most of the attributes available +from the FSO Drive object properties warn "GetVolumeInformation($drive) failed: $^E\n" unless Win32 +API::File::GetVolumeInformation("${drive}/", my $osVolName, 99, my $o +uSerialNum, my $ouMaxNameLen, my $ouFsFlags, my $osFsType, 99 ); my $hexSerialNum = sprintf '%X', $ouSerialNum; #print "GetVolumeInformation(${drive}/) => $osVolName, $ouSeri +alNum = 0x$hexSerialNum, $ouMaxNameLen, $ouFsFlgs, $osFsType"; #... except size info, which can be gotten through Win32::Driv +eInfo my @space = Win32::DriveInfo::DriveSpace($drive); #print "DriveSpace($drive) = ", join ', ', @space; $retval .= "$drive*$fsotype*$hexSerialNum*$osFsType*$osVolName +*$space[5]*$space[6]\n"; } print $retval; } # GetDriveInfoJS(); # this is the GetDriveInfo sub from [harangsolt33] +'s answer (so not repeated here) GetDriveInfoPL();

      and the results:

      JS Gets: C:*2*BE093728*NTFS*OS*1001296752640*719091134464 J:*3*BBA13F0F*NTFS*MyNAS*2984210202624*2221555318784 T:*3*448F8317*exFAT*OffBrand Cloud Drive*54760833024*54619620864 Perl Gets: C:*2*BE093728*NTFS*OS*1001296752640*719091134464 J:*3*BBA13F0F*NTFS*MyNAS*2984210202624*2221555318784 T:*3*448F8317*exFAT*OffBrand Cloud Drive*54760833024*54619620864
      (volume labels obfuscated)

      Well, it runs. I have to be honest, it's a torturous way to go about getting drive info; writing a temp file, executing a different language's interpreter, and so on. But it's a trick some of us might not have seen before (cscript.exe invoked by perl). That aspect of your code intrigues me, I'll file it away for future reference.

      Jun 08, 2025 at 18:37 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://11165216]
Approved by davies
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (3)
As of 2025-06-22 05:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.