http://www.perlmonks.org?node_id=361769

traveler has asked for the wisdom of the Perl Monks concerning the following question:

I have a need to always have a USB thumb drive assigned the same drive letter under Win32. For example, when the drive is inserted, it is assigned the letter U: (or some other) regardless of other drives attached at that instant. The script needs to be a service as it needs to work even if no user is logged in (especially if no user is logged in). I know scripts written with ActiveState can be installed as services, but looking at their docs and those from other sources, I cannot figure out where to start. Can someone give me some hints about where to start? (I am more a *nix programmer than a Win32 programmer which is why I am lost.)

Thanks!

Replies are listed 'Best First'.
Re: USB Drive Letter Assignment in Win32
by BrowserUk (Patriarch) on Jun 06, 2004 at 17:30 UTC

    You might be able to use Win32::API and SetVolumeMountPoint to do this. There is a little more information here and some sample C code here.

    You might also get away with shelling out to the subst command

    P:\test>help subst Associates a path with a drive letter. SUBST [drive1: [drive2:]path] SUBST drive1: /D drive1: Specifies a virtual drive to which you want to assign + a path. [drive2:]path Specifies a physical drive and path you want to assig +n to a virtual drive. /D Deletes a substituted (virtual) drive. Type SUBST with no parameters to display a list of current virtual dri +ves.

    which might be simpler, but would require you to detect where the USB drive was so that you could subst it to a well-known place.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
Re: USB Drive Letter Assignment in Win32
by shonorio (Hermit) on Jun 06, 2004 at 23:13 UTC
    Hi traveler

    Maybe you could use one code as like I'm sending, of course you must adapt somethings (like remove die), but they will running as like services (you must install as services first, take a look on Win32::Daemon POD) and could change the letter of you USB driver.

    I'm not sure how Windows will show the driver after you plug in on computer, because this I wrote the GetLogicalDisk to verify anything about this driver and change letter if necessary.

    use Win32; use strict; use Win32::OLE qw( in ); use Win32::Daemon; use Win32::API::Prototype; # Declare api ApiLink( 'kernel32.dll', 'BOOL DeleteVolumeMountPoint( LPCTSTR lpszVol +umeMountPoint )' ) || die; ApiLink( 'kernel32.dll', 'BOOL SetVolumeMountPoint( LPCTSTR lpszVolume +MountPoint, LPCTSTR lpszVolumeName ) ' ) || die; ApiLink( 'kernel32.dll', 'BOOL GetVolumeNameForVolumeMountPoint( LPCT +STR lpszVolumeMountPoint, LPTSTR lpszVolumeName, DWORD cchBufferLen +gth )' ) || die; my ($State); my $SERVICE_SLEEP_TIME = 20; # 20 milliseconds my $PrevState = SERVICE_START_PENDING; # Start one loop to manage the services while( SERVICE_STOPPED != ( $State = Win32::Daemon::State() ) ) { if( SERVICE_START_PENDING == $State ) { # Initialization code Win32::Daemon::State( SERVICE_RUNNING ); $PrevState = SERVICE_RUNNING; } elsif( SERVICE_STOP_PENDING == $State ) { Win32::Daemon::State( SERVICE_STOPPED ); } elsif( SERVICE_PAUSE_PENDING == $State ) { # "Pausing..."; Win32::Daemon::State( SERVICE_PAUSED ); $PrevState = SERVICE_PAUSED; next; } elsif( SERVICE_CONTINUE_PENDING == $State ) { # "Resuming..."; Win32::Daemon::State( SERVICE_RUNNING ); $PrevState = SERVICE_RUNNING; next; } elsif( SERVICE_STOP_PENDING == $State ) { # "Stopping..."; Win32::Daemon::State( SERVICE_STOPPED ); $PrevState = SERVICE_STOPPED; next; } elsif( SERVICE_RUNNING == $State ) { my $VolumeMountPoint = GetLogicalDisk(); # if the drive is not U:, change if ( $VolumeMountPoint ne "U:" ) { my $VolumeName = NewString( 256 ); if (! GetVolumeNameForVolumeMountPoint ("$VolumeMountPoint +\\", $VolumeName, 256) ) { print Win32::GetLastError(); } else { $VolumeName =~ /(\x00)/; $VolumeName = $`; # Remove n +ull } if (! DeleteVolumeMountPoint("$VolumeMountPoint\\") ) { print Win32::GetLastError(); } if (! SetVolumeMountPoint ("U:\\", $VolumeName )) { print Win32::GetLastError(); } } } else { # Got an unhandled control message. Set the state to # whatever the previous state was. Win32::Daemon::State( $PrevState ); } # Check for any outstanding commands. Pass in a non zero +value # and it resets the Last Message to SERVICE_CONTROL_NONE. if( SERVICE_CONTROL_NONE != ( my $Message = Win32::Daemon::Que +ryLastMessage( 1 ) ) ) { if( SERVICE_CONTROL_INTERROGATE == $Message ) { # Got here if the Service Control Manager is requesting # the current state of the service. This can happen for # a variety of reasons. Report the last state we set. Win32::Daemon::State( $PrevState ); } elsif( SERVICE_CONTROL_SHUTDOWN == $Message ) { # Yikes! The system is shutting down. We had better clean up # and stop. # Tell the SCM that we are preparing to shutdown and that we + expect # it to take 25 seconds (so don't terminate us for at least +25 seconds)... Win32::Daemon::State( SERVICE_STOP_PENDING, 25000 ); } } # Snooze for awhile so we don't suck up cpu time... Win32::Sleep( $SERVICE_SLEEP_TIME ); } # We are done so close down... Win32::Daemon::StopService(); sub GetLogicalDisk { # Here, you could do a query to verify if the USB driver in on Windows +, # look at http://msdn.microsoft.com/library/default.asp?url=/library/e +n-us/wmisdk/wmi/win32_logicaldisk.asp # site to verify wich value you could use, like serialnumber or volume + name my $WMIServices = Win32::OLE->GetObject ( "winmgmts:{impersonation +Level=impersonate, (security)}//./root/cimv2" ) || die; my $DriveCollection = $WMIServices->ExecQuery ( 'select * from Win +32_LogicalDisk where VolumeSerialNumber = "2CBE1114"' ) || die "Query + Failed"; foreach my $Drive ( in( $DriveCollection ) ) { return $Drive->{'Name'}; } }
    Solli Moreira Honorio
    Sao Paulo - Brazil
Re: USB Drive Letter Assignment in Win32
by Zero_Flop (Pilgrim) on Jun 06, 2004 at 18:20 UTC
    Is there a particular reason you need it to be located at a set location? It may be easier if the program that needs to access the thumb dive polls the drive names of the attached drives and then connects to the correct drive. This would make the application more portable in system that may have multiple drives or network drives.

    Not sure if this is for something just for your use or for distribution.

    Good Luck.
      Actually, after long consideration, I suppose not. I could look for it every time, if it is not too long a process. I need the data on the drive available at the very start of the login process so if I could find it, it would be alright. Any idea how to find a thumb drive?

        Any idea how to find a thumb drive?

        You could try putting a file of certain name on the drive and search for that in the root of every drive. Else, you could just find the last valid drive letter, and you have some chance that it is the usb device. Or else you could just find the drive with the smallest capacity (excuding floppies), but that may fail to work later if usb devices get cheaper.

        Sorry for the late responce--

        Give the drive a Label "USBDrive" then...
        use Win32::AdminMisc; %Info = Win32::AdminMisc::GetVolume($drive); print "Drive label is $Info{Volume}\n";
        Cycle through all the attached drives and then you will be able to find the drive you are looking for.
        See the doc for complete info:

        http://www.roth.net/perl/adminmisc/
Re: USB Drive Letter Assignment in Win32
by revdiablo (Prior) on Jun 06, 2004 at 16:39 UTC

    This is just a tad OT, but you can set a drive's letter in Win2k/XP/etc by right clicking on My Computer, selecting Manage, and going to the Disk Management section. You can then click on a drive and set it to a specific drive letter via its properties.

    HTH

      Thanks. Yes, I know how to set the letter manually. I need to do it under program control, when nobody is logged in. I need to do it with a perl script as I don't have Visual Studio on the PC in question.
Re: USB Drive Letter Assignment in Win32
by cLive ;-) (Prior) on Jun 07, 2004 at 05:41 UTC
    Errr, this is a stab in the dark, but I'd be curious to know the answer to this anyway if anyone knows.

    Does the autorun "feature" only work on CDs? If not, why not use autorun to start whatever you're trying to do rather than have a service listening?

    That way you wouldn't need the service in the first place. Or would you? Not being a Windows dev person I have absolutely no idea, but I can immediately think of a couple of practical applications for this if it's possible.

    cLive ;-)

      Oh, yes, there's autorun for USB devices. You may want to disable that feature.

      Abigail

      Slightly off-topic, unless the script will be used in the wild.

      Autorun with USB media sounds excellently geeky, like arstechnica.com's recent USB drive comparison where they made a RAID 0 drive with two different USB drives on OSX :) However, I like many people, have turned off autorun, and I can't even remember how to turn it back on! This stops weird things happening when you insert music CDs which also have videos and you don't have to wait for some install interface to appear when you only want to find one particular file on a software CD.

      How can you feel when you're made of steel? I am made of steel. I am the Robot Tourist.

      Robot Tourist, by Ten Benson

      Yes it does! Add a file called autorun.inf to the root of the USB drive and reference an executable that you want to run. Autorun however only happens when the USB drive is inserted and the machine is logged on. The autorun feature can also be switched off by the user. These restrictions may limit the use of this feature to answer your problem.

      autorun.inf

      [autorun] open=perl.exe auotorun.pl

      Out of interest, you could actually copy a Perl installation onto the USB drive and use that to run the script. This would be particularly useful for doing machine audits. You just turn up, insert the USB drive. The autorun feature starts the Perl script which collects the audit information. The only thing that I haven't figured out is how to programatically unmount the USB drive once you are done.

Re: USB Drive Letter Assignment in Win32
by inman (Curate) on Jun 07, 2004 at 10:35 UTC
    Running a Perl script as a service is actually very easy and requires no extra coding. The only thing that I have noticed with the following method is that the script doesn't really shut down. It is just stopped.

    Any script can be run as a service if you use the srvany application that comes with the Windows Reource Kit tools. Download the resource kit from microsoft and read the documentation regarding InstSrv.exe and SrvAny.exe. The first tool installs a service. The service that you install is SrvAny. Registry settings for SrvAny tell it which application to run as a service.

    The following are some notes that I made for a Perl App that is installed as a service.

    Manual Installation =================== Manual installation requires that the INSTSRV.EXE utility is used to i +nstall the service. The Service is subsequently configured by creating registry s +ettings. 1. run INSTSRV.EXE SERVICENAME C:\path\SRVANY.EXE 2. configure the parameters etc. in the registry as described below 3. Start the service Registry Settings ================= [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SearchWatcher\Pa +rameters] "Application"="C:\\Perl\\bin\\Perl.exe" "AppParameters"="SearchWatcher.pl SearchWatcher.ini" "AppDirectory"="C:\\SearchWatcher\\SearchWatcher" Application is the executable to run. AppParameters are any parameters that are to be passed to the applicat +ion AppDirectory is the working directory to use.
      Hi inman

      I do not recomend to use 'srvany.exe' to make Perl system running as services for 2 reasons:

      1o. System running as a services is little be different of normal system, and if we use one external program to run as a service we don't take care about this difference
      2o. It's so easy to use Win32::Daemon module, and you can take control of Services Queue Message, where you can do something before stop services or shutdown Windows.

      On my opinion, if we don't want take care about extra coding we can use the Windows Schedule. I think it's better than use 'srvany.exe'.

      That's my opinion

      Solli Moreira Honorio
      Sao Paulo - Brazil
Re: USB Drive Letter Assignment in Win32
by meetraz (Hermit) on Jun 07, 2004 at 17:18 UTC
    Traveler,
    ActiveState sells a product called Perl Development Kit (PDK). There are several excellent utilities in there, that I use every day. One of which is called "PerlSvc" and will package a perl script up into an EXE and allow it to be installed as a Windows service.

    One benefit of this over Win32::Daemon and the others is that the EXE file is self-contained. (You can install the service on any computer without having to install Perl first)

      Or if you don't want to spend money, use cpan:://Win32::Daemon with PAR to package it into a self contained exe. With a little cleverness, you can get it to extract all the bits of perl you need into an install directory and add the service..
Re: USB Drive Letter Assignment in Win32
by paulbort (Hermit) on Jun 07, 2004 at 17:53 UTC
    Second the use of a file to make finding the drive easier, but another option, if you're already using the Win32 API, would be to set a volume label on the USB drive. That's kind of what they're for.

    --
    Spring: Forces, Coiled Again!

      The Perl Deverlopment kit also runs $200, and that's a bit much to avoid learning Win32::Daemon or Srvany.

      Traveller, Srvany is embarassingly easy to use, so if you just need it running all the time and don't care to get Down and Dirty with Windows, it's the way to go.

      Win32::Daemon allows to to run a real service that will respond to starts and stops, configuration changes, etc, etc, etc.... Jenda also has a Win32::Daemon::Simple module. The only real drawback here is that there is extra coding to put in, and you start to understand the Windows internals, and how services act.

      It's my experience that the Roth module takes longer to get used to, as it wants you to rearrange your code, but in the long run it is more elegant than the "Simple" module. (As the term simple implies...)

Re: USB Drive Letter Assignment in Win32
by traveler (Parson) on Jun 07, 2004 at 14:31 UTC
    Wow, everybody! Thanks for all the help. I am going to try to put all this together and see what I come up with. Thank you very much!