How can i enumerate a list of all logical volumes on a disk? I want the name of the volume that is suitable for opening with CreateFile
.
What have you tried?
I've used the FindFirstVolume
/FindNextVolume
API to enumerate a list of volumes. It returns a list of names such as:
\\?\Volume{0b777018-3313-11e2-8ccd-806e6f6e6963}\
\\?\Volume{0b777019-3313-11e2-8ccd-806e6f6e6963}\
\\?\Volume{758a2cf2-cf3a-11e4-8dce-c86000d0b92a}\
\\?\Volume{4f81d34b-34f4-11e2-9f6e-c86000d0b92a}\
But none of those volume names are valid volume names. That is, none of those names can be passed to CreateFile
to open the volume:
0x00000003 (The system cannot find the path specified)
The question might be how do i convert the thing returned by FindFirstVolume
into a volume name?
But the real question is how do i enumerate volumes in the first place?
Why not just use \\.\C:
?
I wasn't asking how to hard-code a volume name; i was asking how to enumerate volume names.
Besides, not every volume has a drive letter, e.g.:
\\?\Volume{0b777019-3313-11e2-8ccd-806e6f6e6963}\
==>\\.\C:
\\?\Volume{758a2cf2-cf3a-11e4-8dce-c86000d0b92a}\
==>\\.\D:
\\?\Volume{0b777018-3313-11e2-8ccd-806e6f6e6963}\
==> the system reserved volume with no drive letter\\?\Volume{4f81d34b-34f4-11e2-9f6e-c86000d0b92a}\
==> a CD ROM that is mounted in a folder
I swear there is an API to enumerate volumes.
GetLogicalDriveStrings
The problem with GetLogicalDriveStrings function is that it only returns logical drives:
C:\
D:\
and not volumes. In my case it misses two volumes:
- System Reserved
- D:\CDROM
that FindFirstVolume
does correctly return.
Converting Volume to drive number
- Open the volume
\\?\Volume{0b777019-3313-11e2-8ccd-806e6f6e6963}
using CreateFile - Call DeviceIoControl with the
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
control code - This returns a series of
DISK_EXTENT
structures
DISK_EXTENT = record
DiskNumber: DWORD;
StartingOffset: Int64;
ExtentLength: Int64;
end;
from which you can get the DiskNumber
s.
function GetVolumeDiskExtents(VolumeName: string): TArray<DISK_EXTENT>;
var
hVolume: THandle;
vde: PVolumeDiskExtents;
bufferSize: Integer;
bytesReturned: DWORD;
le: DWORD;
begin
SetLength(Result, 0);
{
CreateFile requires the trailing backslash of a volume name removed.
While Volume API functions require the trailing backslash.
}
// VolumeName := ExcludeTrailingBackslash(VolumeName);
bufferSize := SizeOf(vde)+2048*SizeOf(DISK_EXTENT);
GetMem(vde, bufferSize);
try
hVolume := CreateFile(PChar(VolumeName),
GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hVolume = INVALID_HANDLE_VALUE) then
RaiseLastOSError;
try
if not (DeviceIoControl(hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nil, 0,
vde, bufferSize,
{var}bytesReturned, nil)) then
begin
le := GetLastError;
if le = ERROR_INVALID_FUNCTION then //ie. CD-ROM
Exit;
raise Exception.CreateFmt('Could not get volume disk extents for volume "%s": %s (0x%s)', [VolumeName, SysErrorMessage(le), IntToHex(le, 8)]);
end;
finally
CloseHandle(hVolume);
end;
SetLength(Result, vde^.NumberOfDiskExtents);
Move(vde^.Extents[0], Result[0], SizeOf(DISK_EXTENT)*vde^.NumberOfDiskExtents);
finally
FreeMem(vde);
end;
end;