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