uses Winapi.Windows,System.SysUtils,System.Types,System.UITypes,
Vcl.Dialogs;
type
{ ATA_PASS_THROUGH_EX }
_ATA_PASS_THROUGH_EX =
packed record
Length: Word;
AtaFlags: Word;
PathId: UCHAR;
TargetId: UCHAR;
Lun: UCHAR;
ReservedAsUchar: UCHAR;
DataTransferLength: ULONG;
TimeOutValue: ULONG;
ReservedAsUlong: ULONG;
DataBufferOffset: ULONG_PTR;
PreviousTaskFile:
array [0..7]
of UCHAR;
CurrentTaskFile:
array [0..7]
of UCHAR;
end;
{$EXTERNALSYM _ATA_PASS_THROUGH_EX}
ATA_PASS_THROUGH_EX = _ATA_PASS_THROUGH_EX;
{$EXTERNALSYM ATA_PASS_THROUGH_EX}
TAtaPassThroughEx = _ATA_PASS_THROUGH_EX;
PAtaPassThroughEx = ^TAtaPassThroughEx;
{ ATAIdentifyDeviceQuery }
TATAIdentifyDeviceQuery =
packed record
header: ATA_PASS_THROUGH_EX;
data:
array [0..255]
of Word;
end;
const
{$IF RTLVersion < 22.0}
FILE_DEVICE_CONTROLLER = $00000004;
{$EXTERNALSYM FILE_DEVICE_CONTROLLER}
FILE_READ_ACCESS = $0001;
{$EXTERNALSYM FILE_READ_ACCESS}
FILE_WRITE_ACCESS = $0002;
{$EXTERNALSYM FILE_WRITE_ACCESS}
{$IFEND}
ATA_FLAGS_DRDY_REQUIRED = $01;
ATA_FLAGS_DATA_IN = $02;
ATA_FLAGS_DATA_OUT = $04;
ATA_FLAGS_48BIT_COMMAND = $08;
ATA_FLAGS_USE_DMA = $10;
ATA_FLAGS_NO_MULTIPLE = $20;
IOCTL_SCSI_BASE = FILE_DEVICE_CONTROLLER;
IOCTL_ATA_PASS_THROUGH = (IOCTL_SCSI_BASE
shl 16)
or
((FILE_READ_ACCESS
or FILE_WRITE_ACCESS)
shl 14)
or
($040B
shl 2)
or
(METHOD_BUFFERED);
{$IF RTLversion < 22.0}
const
FILE_READ_DATA = $0001;
{$EXTERNALSYM FILE_READ_DATA}
FILE_READ_ATTRIBUTES = $0080;
{$EXTERNALSYM FILE_READ_ATTRIBUTES}
FILE_DEVICE_MASS_STORAGE = $0000002d;
{$EXTERNALSYM FILE_DEVICE_MASS_STORAGE}
IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;
{$EXTERNALSYM IOCTL_STORAGE_BASE}
FILE_ANY_ACCESS = 0;
{$EXTERNALSYM FILE_ANY_ACCESS}
METHOD_BUFFERED = 0;
{$EXTERNALSYM METHOD_BUFFERED}
IOCTL_STORAGE_QUERY_PROPERTY = (IOCTL_STORAGE_BASE
shl 16)
or
(FILE_ANY_ACCESS
shl 14)
or
($0500
shl 2)
or
(METHOD_BUFFERED);
{$EXTERNALSYM IOCTL_STORAGE_QUERY_PROPERTY}
{$IFEND}
type
{ STORAGE_PROPERTY_ID }
_STORAGE_PROPERTY_ID = (
StorageDeviceProperty = 0,
StorageAdapterProperty = 1,
StorageDeviceIdProperty = 2,
StorageDeviceUniqueIdProperty = 3,
StorageDeviceWriteCacheProperty = 4,
StorageMiniportProperty = 5,
StorageAccessAlignmentProperty = 6,
StorageDeviceSeekPenaltyProperty = 7,
StorageDeviceTrimProperty = 8,
StorageDeviceWriteAggregationProperty = 9,
StorageDeviceDeviceTelemetryProperty = 10
);
{$EXTERNALSYM _STORAGE_PROPERTY_ID}
STORAGE_PROPERTY_ID = _STORAGE_PROPERTY_ID;
{$EXTERNALSYM STORAGE_PROPERTY_ID}
TStoragePropertyId = _STORAGE_PROPERTY_ID;
PStoragePropertyId = ^TStoragePropertyId;
{ STORAGE_QUERY_TYPE }
_STORAGE_QUERY_TYPE = (
PropertyStandardQuery = 0,
PropertyExistsQuery = 1,
PropertyMaskQuery = 2,
PropertyQueryMaxDefined = 3
);
{$EXTERNALSYM _STORAGE_QUERY_TYPE}
STORAGE_QUERY_TYPE = _STORAGE_QUERY_TYPE;
{$EXTERNALSYM STORAGE_QUERY_TYPE}
TStorageQueryType = _STORAGE_QUERY_TYPE;
PStorageQueryType = ^TStorageQueryType;
{ STORAGE_PROPERTY_QUERY }
_STORAGE_PROPERTY_QUERY =
packed record
PropertyId: DWORD;
QueryType: DWORD;
AdditionalParameters:
array[0..9]
of Byte;
end;
{$EXTERNALSYM _STORAGE_PROPERTY_QUERY}
STORAGE_PROPERTY_QUERY = _STORAGE_PROPERTY_QUERY;
{$EXTERNALSYM STORAGE_PROPERTY_QUERY}
TStoragePropertyQuery = _STORAGE_PROPERTY_QUERY;
PStoragePropertyQuery = ^TStoragePropertyQuery;
{ DEVICE_SEEK_PENALTY_DESCRIPTOR }
_DEVICE_SEEK_PENALTY_DESCRIPTOR =
packed record
Version: DWORD;
Size: DWORD;
IncursSeekPenalty: ByteBool;
Reserved:
array[0..2]
of Byte;
end;
{$EXTERNALSYM _DEVICE_SEEK_PENALTY_DESCRIPTOR}
DEVICE_SEEK_PENALTY_DESCRIPTOR = _DEVICE_SEEK_PENALTY_DESCRIPTOR;
{$EXTERNALSYM DEVICE_SEEK_PENALTY_DESCRIPTOR}
TDeviceSeekPenaltyDescriptor = _DEVICE_SEEK_PENALTY_DESCRIPTOR;
PDeviceSeekPenaltyDescriptor = ^TDeviceSeekPenaltyDescriptor;
type
{ DISK_EXTENT }
_DISK_EXTENT =
packed record
DiskNumber: DWORD;
StartingOffset: LARGE_INTEGER;
ExtentLength: LARGE_INTEGER;
Reserved:
array [0..3]
of Byte;
end;
{$EXTERNALSYM _DISK_EXTENT}
DISK_EXTENT = _DISK_EXTENT;
{$EXTERNALSYM DISK_EXTENT}
TDiskExtent = _DISK_EXTENT;
PDiskExtent = ^TDiskExtent;
{ VOLUME_DISK_EXTENTS }
_VOLUME_DISK_EXTENTS =
packed record
NumberOfDiskExtents: DWORD;
Reserved:
array [0..3]
of Byte;
Extents:
array [0..0]
of DISK_EXTENT;
end;
{$EXTERNALSYM _VOLUME_DISK_EXTENTS}
VOLUME_DISK_EXTENTS = _VOLUME_DISK_EXTENTS;
{$EXTERNALSYM VOLUME_DISK_EXTENTS}
TVolumeDiskExtents = VOLUME_DISK_EXTENTS;
PVolumeDiskExtents = ^TVolumeDiskExtents;
{$IF RTLVersion < 22.0}
const
IOCTL_VOLUME_BASE = $00000056;
{$EXTERNALSYM IOCTL_VOLUME_BASE}
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = (IOCTL_VOLUME_BASE
shl 16)
or
(FILE_ANY_ACCESS
shl 14)
or
(0
shl 2)
or
(METHOD_BUFFERED);
{$EXTERNALSYM IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS}
{$IFEND}
function HasNominalMediaRotationRate(
const PhysicalDrivePath:
String): Boolean;
function HasNoSeekPenalty(
const PhysicalDrivePath:
String): Boolean;
procedure PathnameToPhysicalDriveNumber(
const Path:
String;
var PhysicalDrives: TIntegerDynArray);
function IstSSD(LW:Char):Boolean;
implementation
function HasNominalMediaRotationRate(
const PhysicalDrivePath:
String): Boolean;
var
h: THandle;
ATAIdentifyDeviceQuery: TATAIdentifyDeviceQuery;
RSize: DWORD;
begin
h := CreateFile(PChar(PhysicalDrivePath),GENERIC_READ
or GENERIC_WRITE,
FILE_SHARE_READ
or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if h = INVALID_HANDLE_VALUE
then
begin
RaiseLastOSError;
end;
try
FillChar(ATAIdentifyDeviceQuery,SizeOf(ATAIdentifyDeviceQuery),0);
with ATAIdentifyDeviceQuery
do
begin
header.Length := SizeOf(header);
header.AtaFlags := ATA_FLAGS_DATA_IN;
header.DataTransferLength := SizeOf(data);
header.TimeOutValue := 3;
// sec
header.DataBufferOffset := SizeOf(header);
header.CurrentTaskFile[6] := $
EC;
// ATA IDENTIFY DEVICE command
end;
RSize := 0;
if DeviceIoControl(h,IOCTL_ATA_PASS_THROUGH,
@ATAIdentifyDeviceQuery,SizeOf(ATAIdentifyDeviceQuery),
@ATAIdentifyDeviceQuery,SizeOf(ATAIdentifyDeviceQuery),
RSize,
nil) = False
then
begin
RaiseLastOSError;
end;
Result := (ATAIdentifyDeviceQuery.data[217] = 1);
finally
CloseHandle(h);
end;
end;
function HasNoSeekPenalty(
const PhysicalDrivePath:
String): Boolean;
var
h :THandle;
StoragePropertyQuery: TStoragePropertyQuery;
DeviceSeekPenaltyDescriptor: TDeviceSeekPenaltyDescriptor;
RSize: DWORD;
begin
h := CreateFile(PChar(PhysicalDrivePath),FILE_READ_ATTRIBUTES,
FILE_SHARE_READ
or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if h = INVALID_HANDLE_VALUE
then
begin
RaiseLastOSError;
end;
try
with StoragePropertyQuery
do
begin
PropertyId := Ord(StorageDeviceSeekPenaltyProperty);
QueryType := Ord(PropertyStandardQuery);
end;
FillChar(DeviceSeekPenaltyDescriptor,SizeOf(DeviceSeekPenaltyDescriptor),0);
RSize := 0;
if DeviceIoControl(h,IOCTL_STORAGE_QUERY_PROPERTY,
@StoragePropertyQuery,SizeOf(StoragePropertyQuery),
@DeviceSeekPenaltyDescriptor,SizeOf(DeviceSeekPenaltyDescriptor),
RSize,
nil) = False
then
begin
RaiseLastOSError;
end;
Result :=
not DeviceSeekPenaltyDescriptor.IncursSeekPenalty;
finally
CloseHandle(h);
end;
end;
procedure PathnameToPhysicalDriveNumber(
const Path:
String;
var PhysicalDrives: TIntegerDynArray);
var
h: THandle;
I: Integer;
MountPoint:
String;
VolumeName:
String;
Size: DWORD;
RSize: DWORD;
P: PVolumeDiskExtents;
lpFilePart : PWideChar;
lpBuffer : PWideChar;
begin
SetLength(PhysicalDrives,0);
lpBuffer :=
nil;
{ Pathname to mount point }
Size := GetFullPathName(PWideChar(Path),0,lpBuffer,lpFilePart);
SetLength(MountPoint,Size);
if GetVolumePathName(PAnsiChar(Path),PAnsiChar(MountPoint),Size) = False
then
begin
RaiseLastOSError;
end;
SetLength(MountPoint,StrLen(PWideChar(MountPoint)));
{ Mount point to logical volume name }
Size := 50;
// Recomended size from http://msdn.microsoft.com/en-us/library/windows/desktop/aa364994.aspx
SetLength(VolumeName,Size);
if GetVolumeNameForVolumeMountPoint(PAnsiChar(MountPoint),PAnsiChar(VolumeName),Size) = False
then
begin
RaiseLastOSError;
end;
SetLength(VolumeName,StrLen(PChar(VolumeName)));
VolumeName := ExcludeTrailingPathDelimiter(VolumeName);
{ Open volume }
h := CreateFile(PChar(VolumeName),FILE_READ_ATTRIBUTES,
FILE_SHARE_READ
or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if h = INVALID_HANDLE_VALUE
then
begin
RaiseLastOSError;
end;
try
Size := SizeOf(TVolumeDiskExtents);
P := AllocMem(Size);
try
FillChar(P^,Size,0);
RSize := 0;
if DeviceIoControl(h,IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nil,0,
P,Size,
RSize,
nil) = False
then
begin
if GetLastError <> ERROR_MORE_DATA
then
begin
RaiseLastOSError;
end;
Size := SizeOf(TVolumeDiskExtents) +
SizeOf(DISK_EXTENT) * (P^.NumberOfDiskExtents - 1);
ReallocMem(P,Size);
FillChar(P^,Size,0);
if DeviceIoControl(h,IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nil,0,
P,Size,
RSize,
nil) = False
then
begin
RaiseLastOSError;
end;
end;
SetLength(PhysicalDrives,P^.NumberOfDiskExtents);
for I := 0
to P^.NumberOfDiskExtents - 1
do
begin
PhysicalDrives[I] := P^.Extents[I].DiskNumber;
end;
finally
FreeMem(P);
end;
finally
CloseHandle(h);
end;
end;
function IstSSD(LW:Char):Boolean;
var
Index: Integer;
Filename:
String;
PhysicalDrives: TIntegerDynArray;
PhysicalDrivePath:
String;
IsSSD: Boolean;
begin
Result := False;
Filename := LW + '
:\';
SetLength(PhysicalDrives,0);
PathnameToPhysicalDriveNumber(Filename,PhysicalDrives);
try
IsSSD := False;
for Index := Low(PhysicalDrives)
to High(PhysicalDrives)
do
begin
PhysicalDrivePath := Format('
\\.\PhysicalDrive%d',[PhysicalDrives[
Index]]);
try
IsSSD := IsSSD
or HasNominalMediaRotationRate(PhysicalDrivePath);
except
{ Ignore }
end;
if IsSSD = True
then
begin
exit(True);
end;
end;
finally
SetLength(PhysicalDrives,0);
end;
end;
end.