uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Types,System.UITypes;
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;
//
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(PWideChar(Path),PWideChar(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(PWideChar(MountPoint),PWideChar(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;
procedure TForm1.Button1Click(Sender: TObject);
begin
if IstSSD('C') = true then begin
Label1.Caption := 'yes';
end else begin
Label1.Caption := 'no';
end;
end;
Keine Kommentare:
Kommentar veröffentlichen