Search code examples
delphimappingdisk-partitioning

mapping partitions starting from PhysicalDrive


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.


Solution

  • 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.