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

Hello, I am new to the site, but not to Perl.

I have been working on a script to parse through several data set files and check for errors. These sets are huge and usually take up an entire 750GB hard drive each. Thankfully I get most of the information out of the filename and attributes. As part of my data logging, we want to log the physical drive serial number associated with each data set. More info on this below.

Anyway, every time a problem came up, your site was near the top of a Google search with the answer.

The question:

I have a main script that does the parsing based on drive letter to find out all the file information. I have another script (that I found here) that can use the Win32::OLE->GetObject to get information about DiskDrive, PhysicalMedia, and LogicalDrive, and even return the Serial Number for a given physical drive.

What I can't figure out how to do is determine how the physical drives are mapped to drive letters and incorporate this into the Perl script.

If someone knows a registry entry, windows file, or Perl library/command that can help, I would really appreciate it.
Thanks, Anthony

Replies are listed 'Best First'.
Re: Windows drive letter mapping question
by mr_mischief (Monsignor) on Apr 11, 2008 at 18:06 UTC
    I think what you want is better found in Win32::DriveInfo than in Win32::OLE.

    I think it might be possible from Win32::OLE, but I'm not good enough with Windows APIs to tell you how. There's good information on how to do it from the C API at "How to get the PhysicalDrive number which the logical drive locate:". If you can figure out how that translates to Perl, then hopefully it will help, but I'd recommend trying the existing wheel first.

      I tried looking at Win32::DriveInfo and that did not have all the information I needed. The Serial Number listed there is volume S/N, not physical S/N. I checked out that other link, but I had NO idea how to implement it in Perl.

      I did some digging on WMI in the MSDN and found what I need, but it isn't pretty.

      Drive letter info is given at the Win32_LogicalDisk level. The tree from there is as follows:
      Win32_LogicalDiskToPartition links to
      Win32_DiskPartition, which is referenced by
      Win32_DiskDriveToDiskPartition, which links to
      Win32_DiskDrive, which is referenced by
      Win32_DiskDrivePhysicalMedia, which links to
      Win32_PhysicalMedia, which has the actual hardware S/N.

      Whew! :) So that ain't gonna be pretty, but all the data is there.

      I also saw that newer versions of windows have access to the Win_32_Volume class, which links drive letter to a weird volume string (which looks like a registry entry). However, I could not find how that maps to a physical device, which is the only way, apparently, to get the HW S/N.

      Thanks for the help so far. If anyone has any more advice on shortcuts or different approaches, I'm all ears. Until then, I will plug away at this. --Anthony
Re: Windows drive letter mapping question
by swampyankee (Parson) on Apr 11, 2008 at 19:03 UTC

    You could use system and the net use command.


    Information about American English usage here and here. Floating point issues? Please read this before posting. — emc

Re: Windows drive letter mapping question
by AnthonyC (Novice) on Apr 15, 2008 at 20:39 UTC
    Success! It was actually prettier code than I thought it would be.

    Thanks to slloyd for his "Is a drive USB, IDE or SCSI?" script (found here ) Also, thanks to Mr. Muskrat for his "(Win32) Hard Drive Information" script (found here)

    Here's my script. Feel free to use it or comment. I'm sure it is not the most efficient way to do this, but it works fast enough for my needs. Sorry about the comments, I'll work on my obfuscation later. :-)

    use strict; use warnings; use Win32::OLE qw(in); my $Machine = shift @ARGV || "."; $Machine =~ s/^[\\\/]+//; my $WMIServices = Win32::OLE->GetObject( "winmgmts:{impersonationLevel +=impersonate,(security)}//$Machine") || die "Could not get Win32::OLE + Object.\n"; my %serial; # Serial Number hash based on device name my %device; # Drive Letter Based on Device Name # Populate Serial Hash my $PhysMediaCollection = $WMIServices->InstancesOf( "Win32_PhysicalMe +dia" ); foreach my $PM (in ($PhysMediaCollection)) { my $name = (split(/=/, $PM->{Path_}->relpath))[1]; $serial{eval $name}=$PM->{SerialNumber}; } # Loop Over Hard Disks my $DiskDriveCollection = $WMIServices->InstancesOf( "Win32_DiskDrive" + ); #my $DiskDriveCollection = $WMIServices->ExecQuery("SELECT Caption, De +viceID FROM Win32_DiskDrive"); foreach my $DiskDrive (in($DiskDriveCollection)) { my $cap_disk = $DiskDrive->{Caption}; my $did_disk = $DiskDrive->{DeviceID}; my $query_did = $did_disk; $query_did =~ s/\\/\\\\/sg; # Very important to change slashy form +atting my $query = 'ASSOCIATORS OF ' . '{Win32_DiskDrive.DeviceID="' . $q +uery_did . '"} WHERE AssocClass = Win32_DiskDriveToDiskPartition'; my $DiskPartCollection = $WMIServices->ExecQuery($query); #Loop Over Partitions foreach my $Partition (in($DiskPartCollection)) { my $did_part = $Partition->{DeviceID}; my $query = 'ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\'' +. $did_part . "\'} WHERE AssocClass = Win32_LogicalDiskToPartition"; my $LogDriveCollection = $WMIServices->ExecQuery($query); # Loop Over Logical Drives foreach my $LogDrive (in($LogDriveCollection)) { my $did_log = $LogDrive->{DeviceID}; $device{$did_log} = $did_disk; } # Next Logical Drive } # Next Partition } # Next Hard Disk # Print Results foreach my $letter (keys %device) { my $sn = $serial{$device{$letter}}; print "Drive $letter maps to drive with S/N $sn \n"; }

      This doesn't work, are you sure you got the correct results returned?