0

I want to map all partitions from all drives in Windows (the ones who are not mapped already). I mean I want to assign to each of them drive letters. I know that you can do it with FindFirstVolume, FindNextVolume, FindVolumeClose but there are situations where you can't use them. I tried with QueryDosDevice, same thing.

The idea is to start from \.\PhysicalDrive[n], find out the partitions and map them. I know it's doable because I saw a program that can do that. But I don't like it, because it maps hidden partitions also.

Does someone know a way...? Thank you.

Chris Gerken
  • 16,221
  • 6
  • 44
  • 59
John Mirror
  • 193
  • 1
  • 13
  • Can you describe when using FindFirstVolume..FindNextVolume doesn't work? – Remko Jan 12 '11 at 19:25
  • So your question is how to iterate through all partitions and not how to mount them? – Ritsaert Hornstra Jan 12 '11 at 19:29
  • @Remko They don't work on some versions of Windows where drivers for some devices (like USB drives) are added dynamically and those functions can't "see" them. @Ritsaert I said I want to mount them (all). – John Mirror Jan 12 '11 at 20:29
  • @John So you question is actually about the situations where FindFirstVolume, FindNextVolume don't give you what you need. – David Heffernan Jan 12 '11 at 20:53
  • @John take a look at http://stackoverflow.com/questions/3788057/how-does-one-programmatically-mount-a-drive-in-windows – David Heffernan Jan 12 '11 at 20:55
  • @David Yes, there are some similarities... – John Mirror Jan 12 '11 at 21:04
  • Sorry but I don't understand 100% that topic. Can you please show me how to apply to my problem...? Thank you. – John Mirror Jan 13 '11 at 02:52
  • @David I looked closely in the morning at that topic. Are you sure that it applies to my problem even if I can't get a Volume GUID with any function...? – John Mirror Jan 13 '11 at 07:38
  • @John I've no idea whether it applies to your problem because your problem isn't clear to me. – David Heffernan Jan 13 '11 at 09:18
  • @David Ok... I will try to explain again: on some computers for a few storage devices I have to load drivers after Windows has started. The devices appear in Disk management as Harddisk1..n, they are not mapped, but QueryDosDevices and FindFirst/NextVolume can't find their GUID. I can map them manually but I would like to map them automatically... – John Mirror Jan 13 '11 at 10:00

3 Answers3

3

I did it :) I made a program who adds or removes drive letters when it's started - if one or more storage drives were added or removed from the computer:

program MapDrives;

uses Windows;

type
   TPARTITION_INFORMATION = record
      StartingOffset: _LARGE_INTEGER; //TLargeInteger;
      PartitionLength: _LARGE_INTEGER; //TLargeInteger;
      HiddenSectors: DWORD;
      PartitionNumber: DWORD;
      PartitionType: BYTE;
      BootIndicator: BOOLEAN;
      RecognizedPartition: BOOLEAN;
      RewritePartition: BOOLEAN;
   end;

function IntToStr(Value: Integer): string;
begin
   if Value < 10 then
      Result := Char(Value + 48)
   else
      Result := Char(Value div 10 + 48) + Char(Value + 48);
end;

function GetNextAvailableLetter: AnsiChar;
var Drives, mask: DWord;
   i: Integer;
begin
   Drives := GetLogicalDrives;
   mask := 4;
   Result := 'Z';
   for i := 3 to 26 do //C to Z
   begin
      if mask and Drives = 0 then
      begin
         Result := AnsiChar(64 + i);
         Exit;
      end;
      mask := mask shl 1;
   end;
end;


const IOCTL_DISK_GET_PARTITION_INFO = $0074004;

var i, j, k: Integer;
   H: THandle;
   dwBytesReturned: DWORD;
   BreakCycle, DoMount: Boolean;
   NextLetter: AnsiChar;
   PartitionInformation: TPARTITION_INFORMATION;
   PartitionsInformation: array of TPARTITION_INFORMATION;
   Drives, mask: DWord;
   OldMode: UINT;

begin
   OldMode := SetErrorMode(SEM_FAILCRITICALERRORS); //so it shouldn't ask to insert CD or card

   //gets informations about already mounted partitions
   SetLength(PartitionsInformation, 0);
   Drives := GetLogicalDrives;
   mask := 4;
   for i := 3 to 26 do //C to Z
   begin
      if mask and Drives <> 0 then
      begin
         H := CreateFile(PAnsiChar('\\.\' + Char(64 + i) + ':'), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
         if H <> INVALID_HANDLE_VALUE then
         begin
            SetLength(PartitionsInformation, Length(PartitionsInformation) + 1);
            DeviceIoControl(H, IOCTL_DISK_GET_PARTITION_INFO, nil, 0, @PartitionsInformation[High(PartitionsInformation)], SizeOf(TPARTITION_INFORMATION), dwBytesReturned, nil);
            CloseHandle(H);
         end
         else     //removes unaccessible drives
            DefineDosDevice(DDD_REMOVE_DEFINITION or DDD_RAW_TARGET_PATH, PAnsiChar(string(Char(64 + i) + ':')), nil);
      end;
      mask := mask shl 1;
   end;

   for i := 0 to 99 do
   begin
      H := CreateFile(PAnsiChar('\\.\PhysicalDrive' + IntToStr(i)), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
      if H = INVALID_HANDLE_VALUE then //no more hdd's
         Break;
      CloseHandle(H);
      for j := 1 to 20 do
      begin
         BreakCycle := False;
         NextLetter := GetNextAvailableLetter;
         DefineDosDevice(DDD_RAW_TARGET_PATH or DDD_NO_BROADCAST_SYSTEM, PAnsiChar(string(NextLetter + ':')), PAnsiChar('\Device\Harddisk' + IntToStr(i) + '\Partition' + IntToStr(j)));
         DoMount := True;
         H := CreateFile(PAnsiChar('\\.\' + NextLetter + ':'), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
         if H = INVALID_HANDLE_VALUE then //no more partitions
            BreakCycle := True
         else
         begin
            PartitionInformation.PartitionType := 0;
            DeviceIoControl(H, IOCTL_DISK_GET_PARTITION_INFO, nil, 0, @PartitionInformation, SizeOf(TPARTITION_INFORMATION), dwBytesReturned, nil);
            DoMount := PartitionInformation.PartitionType in [0, 1, 6, 7, 11, 12, 114];
            CloseHandle(H);
         end;
         if DoMount then
         begin
            for k := 0 to High(PartitionsInformation) do  //compare with already mounted partitions
               if (PartitionsInformation[k].StartingOffset.LowPart = PartitionInformation.StartingOffset.LowPart) and
                  (PartitionsInformation[k].StartingOffset.HighPart = PartitionInformation.StartingOffset.HighPart) and
                  (PartitionsInformation[k].StartingOffset.QuadPart = PartitionInformation.StartingOffset.QuadPart) and
                  (PartitionsInformation[k].PartitionLength.LowPart = PartitionInformation.PartitionLength.LowPart) and
                  (PartitionsInformation[k].PartitionLength.HighPart = PartitionInformation.PartitionLength.HighPart) and
                  (PartitionsInformation[k].PartitionLength.QuadPart = PartitionInformation.PartitionLength.QuadPart) and
                  (PartitionsInformation[k].HiddenSectors = PartitionInformation.HiddenSectors) and
                  (PartitionsInformation[k].PartitionType = PartitionInformation.PartitionType) and
                  (PartitionsInformation[k].BootIndicator = PartitionInformation.BootIndicator) and
                  (PartitionsInformation[k].RecognizedPartition = PartitionInformation.RecognizedPartition) then
                  Break;
            DoMount := k > High(PartitionsInformation);
         end;
         DefineDosDevice(DDD_REMOVE_DEFINITION or DDD_RAW_TARGET_PATH, PAnsiChar(string(NextLetter + ':')), nil);
         if (not BreakCycle) and DoMount then
            DefineDosDevice(DDD_RAW_TARGET_PATH, PAnsiChar(string(NextLetter + ':')), PAnsiChar('\Device\Harddisk' + IntToStr(i) + '\Partition' + IntToStr(j)));
         if BreakCycle then
            Break;
      end;
   end;
   SetErrorMode(OldMode); //restore original mode
end.

On the computers that I mentioned it works perfectly.

Thank you guys for all your ideas which helped me to make this code.

If someone notice some bugs or has any good ideas about how to improve it, I'll be glad to fix/implement them.

John Mirror
  • 193
  • 1
  • 13
  • On delphi XE it's render `[dcc32 Error] Project1.dpr(67): E2010 Incompatible types: 'PWideChar' and 'PAnsiChar` . Would you please update this for XE ? – Bianca Feb 10 '15 at 09:21
2

You can do This using WMI.
In library GLibWMI (http://neftali.clubdelphi.com or SourceForge) you can find the TDiskPartitionInfo and TDiskDriveInfo.
The first can give you the created partitions and all of your properties.
Test the Generic Sample and check the results. In an partitioned disk like this:
alt text

You obtains 4 instantes with the properties of 4 partitions like this:

alt text

The library it's totally free and source is avaible. Check the samples.
You can find some other codes to access this information using WMI. If you want use another, you can search for "WMI and Win32_DiskPartition Class" (Link doc).

Excuse-me for mistakes with English.
Regards

1

Maybe my Change DriveLetter commandline tool can help you, at least you can start it with commandline parameters and see if it lists all volumes you expect.

This link might also be useful: Converting a volume name to a devicename

Remko
  • 7,214
  • 2
  • 32
  • 52
  • Thank you, it's a nice program. But the problem is that I want to use it on different computers where I don't know labels + I want to mount USB drives too, not only CDROMs. And that function is good but I think I need the opposite: I know the device (PhysicalDrive[n]) and I want to find Volumes to mount then. – John Mirror Jan 12 '11 at 20:14
  • @John So what exactly is your question? Do you just want to find some unmapped volume letters? Note that there is a difference between mapping volumes and mounting them. Mounting them makes them available to the system. Mapping them gives them a name like C:, D: etc. Unmapped but mounted drives are still accessible using UNC paths. – David Heffernan Jan 12 '11 at 20:28
  • @David Ok, I want to map them. Sorry for my english :( – John Mirror Jan 12 '11 at 20:34
  • @John Now my other question. Do you just want a list of available drive letters so that you can map them to unmapped, mounted volumes? – David Heffernan Jan 12 '11 at 20:37
  • @David. No I don't want a list. I just want to find the Volume names of the partitions from each PhysicalDrive and to map them to available drive letters (I know how to find available drive letters). – John Mirror Jan 12 '11 at 20:46
  • @John OK, you mean you want the UNC name of each mounted volume? – David Heffernan Jan 12 '11 at 20:49
  • @David, if I can map the partitions with them, yes. – John Mirror Jan 12 '11 at 20:53
  • @John: ChDrvLetter can assign a drive letter to *any* volume (harddisk, usb disk, dvd drive etc) but I was really interested if it lists the volumes you have problems with when using it without params. If so you can use the same method that ChDrvLetter tool uses... – Remko Jan 12 '11 at 21:59
  • @Remko Unfortunately it doesn't see better than FindFirstVolume, FindNextVolume, FindVolumeClose. For example it doesn't see the partition from PhysicalDrive1. In Disk Management I can temporarily add a letter to it (until the next reboot). – John Mirror Jan 13 '11 at 02:50