Sorry, I know NOTHING about this subject, so asking for your help. I found this code below on Google, to get physical disk number by drive letter, and despite it works, takes about 4 or 5 seconds to get the result. I'd like to know if there is a faster way and how to do it? Thanks!
function GetPhysicalDiskNumber(Drive: Char): Byte;
function GetLD(Drive: Char): Cardinal;
var
Buffer : String;
begin
Buffer := Format('\\.\%s:',[Drive]);
Result := CreateFile(PChar(Buffer),GENERIC_READ Or GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
If Result = INVALID_HANDLE_VALUE Then
begin
Result := CreateFile(PChar(Buffer),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
end;
end;
type
PDiskInfo = ^TDiskInfo;
TDiskInfo = record
BootStatus,
StartHead : Byte;
StartSecClu : Array[0..1] Of Byte;
ParitionType,
LastHead : Byte;
LastSecClu : Array[0..1] Of Byte;
ABSSector,
TTLSector : Integer;
Reserved : Array[0..47] Of Byte;
Signature : Array[0..1] Of Byte;
end;
TDiskExtent = record
DiskNumber: Cardinal;
StartingOffset: Int64;
ExtentLength: Int64;
end;
DISK_EXTENT = TDiskExtent;
PDiskExtent = ^TDiskExtent;
TVolumeDiskExtents = record
NumberOfDiskExtents: Cardinal;
Extents: array[0..0] of TDiskExtent;
end;
VOLUME_DISK_EXTENTS = TVolumeDiskExtents;
PVolumeDiskExtents = ^TVolumeDiskExtents;
const
FILE_DEVICE_DISK = $00000007;
METHOD_BUFFERED = 0;
FILE_ANY_ACCESS = 0;
IOCTL_DISK_BASE = FILE_DEVICE_DISK;
IOCTL_VOLUME_BASE = DWORD('V');
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = ((IOCTL_VOLUME_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or (0 shl 2) or METHOD_BUFFERED);
var
LD : DWORD;
DiskExtents : PVolumeDiskExtents;
DiskExtent : TDiskExtent;
BytesReturned : Cardinal;
begin
Result := 0;
LD := GetLD(Drive);
If LD = INVALID_HANDLE_VALUE Then Exit;
Try
DiskExtents := AllocMem(Max_Path);
DeviceIOControl(LD,IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,nil,0,DiskExtents,Max_Path,BytesReturned,nil);
If DiskExtents^.NumberOfDiskExtents > 0 Then
begin
DiskExtent := DiskExtents^.Extents[0];
Result := DiskExtent.DiskNumber;
end;
Finally
CloseHandle(LD);
end;
end;
Documentation for CreateFile
states:
• When opening a volume or floppy disk, the dwShareMode parameter must have the FILE_SHARE_WRITE flag.
The code you're using is missing the flag. The code also has a peculiar characteristic in that it does not notify failure. When CreateFile
fails, your GetPhysicalDiskNumber
returns '0', suggesting the result is the first disk.
This is what I think is happening: you're testing on a volume that the system cannot lock against write access and times out while trying to do so (hence the delay). But your function still returns '0', so you think it is working.
In any case, you need the flag. I would, additionally, raise an exception when CreateFile
fails, so that you'd have a clue what is happening.
function GetLD(Drive: Char): Cardinal;
var
Buffer : String;
begin
Buffer := Format('\\.\%s:',[Drive]);
Result := CreateFile(PChar(Buffer), GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
Win32Check(Result <> INVALID_HANDLE_VALUE);
end;
You may choose to have a silent fail though. In that case you can initially set Result
of GetPhysicalDiskNumber
to '-1' for instance, and pass on raising an exception for CreateFile
and DeviceIoControl
.
The code also fails to release the memory it allocates, that's a leak:
...
try
DiskExtents := AllocMem(Max_Path);
try
Win32Check(DeviceIOControl(LD, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, nil, 0,
DiskExtents, Max_Path, BytesReturned, nil));
if DiskExtents^.NumberOfDiskExtents > 0 then
begin
DiskExtent := DiskExtents^.Extents[0];
Result := DiskExtent.DiskNumber;
end;
finally
FreeMem(DiskExtents);
end;
finally
CloseHandle(LD);
...