function GetDeviceHandle(sDeviceName: string): THandle;
begin
Result := CreateFile(PChar('\\.\' + sDeviceName),
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0)
end;
function ScsiHddSerialNumber(DeviceHandle: THandle): string;
{$ALIGN ON}
type
TScsiPassThrough = record
Length: Word;
ScsiStatus: Byte;
PathId: Byte;
TargetId: Byte;
Lun: Byte;
CdbLength: Byte;
SenseInfoLength: Byte;
DataIn: Byte;
DataTransferLength: ULONG;
TimeOutValue: ULONG;
DataBufferOffset: DWORD;
SenseInfoOffset: ULONG;
Cdb: array[0..15] of Byte;
end;
TScsiPassThroughWithBuffers = record
spt: TScsiPassThrough;
bSenseBuf: array[0..31] of Byte;
bDataBuf: array[0..191] of Byte;
end;
{ALIGN OFF}
var
dwReturned: DWORD;
len: DWORD;
Buffer: array[0..SizeOf(TScsiPassThroughWithBuffers) +
SizeOf(TScsiPassThrough) - 1] of Byte;
sptwb: TScsiPassThroughWithBuffers absolute Buffer;
begin
Result := '';
FillChar(Buffer, SizeOf(Buffer), #0);
with sptwb.spt do
begin
Length := SizeOf(TScsiPassThrough);
CdbLength := 6; // CDB6GENERIC_LENGTH
SenseInfoLength := 24;
DataIn := 1; // SCSI_IOCTL_DATA_IN
DataTransferLength := 192;
TimeOutValue := 2;
DataBufferOffset := PChar(@sptwb.bDataBuf) - PChar(@sptwb);
SenseInfoOffset := PChar(@sptwb.bSenseBuf) - PChar(@sptwb);
Cdb[0] := $12; // OperationCode := SCSIOP_INQUIRY;
Cdb[1] := $01; // Flags := CDB_INQUIRY_EVPD; Vital product data
Cdb[2] := $80; // PageCode Unit serial number
Cdb[4] := 192; // AllocationLength
end;
len := sptwb.spt.DataBufferOffset + sptwb.spt.DataTransferLength;
if DeviceIoControl(DeviceHandle, $0004D004, @sptwb, SizeOf(TScsiPassThrough),
@sptwb, len, dwReturned, nil)
and ((PChar(@sptwb.bDataBuf) + 1)^ = #$80) then
SetString(Result, PChar(@sptwb.bDataBuf) + 4,
Ord((PChar(@sptwb.bDataBuf) + 3)^));
end;
/=============================================================
var
hDevice: THandle = 0;
sSerNum, sDeviceName: string;
begin
sDeviceName := ParamStr(1);
if sDeviceName = '' then
begin
WriteLn;
WriteLn('Display SCSI-2 device serial number.');
WriteLn;
WriteLn('Using:');
WriteLn;
if Win32Platform = VER_PLATFORM_WIN32_NT then // Windows NT/2000
WriteLn(' ScsiSN PhysicalDrive0')
else
WriteLn(' ScsiSN C:');
WriteLn(' ScsiSN Cdrom0');
WriteLn(' ScsiSN Tape0');
WriteLn;
Exit;
end;
hDevice := GetDeviceHandle(sDeviceName);
if hDevice = INVALID_HANDLE_VALUE then
WriteLn('Error on GetDeviceHandle: ', SysErrorMessage(GetLastError))
else
try
sSerNum := ScsiHddSerialNumber(hDevice);
if sSerNum = '' then
WriteLn('Error on DeviceIoControl: ',
SysErrorMessageGetLastError))
else
WriteLn('Device ' + sDeviceName
+ ' serial number = "', sSerNum, '"');
finally
CloseHandle(hDevice);
end;
end.
For more information about SCSI commands:
ftp://ftp.t10.org/t10/drafts/scsi-1/
ftp://ftp.t10.org/t10/drafts/spc/
ftp://ftp.t10.org/t10/drafts/spc2/
Программа на Delphi, которая отображает серийный номер SCSI-жесткого диска (HDD) или CD-ROM устройства, подключенного к системе. Программа использует Windows API и функцию DeviceIoControl для отправки SCSI-запроса устройству и получения его серийного номера.
Вот шаг за шагом, как работает программа:
Программа определяет два функциона: GetDeviceHandle и ScsiHddSerialNumber. Первая возвращает handle к указанному устройству, а вторая отправляет SCSI-запрос на устройство и получает его серийный номер.
В основной части программы пользователь приглашается ввести имя устройства, которое он хочет запросить (например, "PhysicalDrive0" или "Cdrom0").
Функция GetDeviceHandle вызывается с именем устройства, введенным пользователем, и возвращает handle к устройству.
Если handle устройства является действительным, программа вызывает функцию ScsiHddSerialNumber, чтобы отправить SCSI-запрос на устройство и получить его серийный номер.
Функция ScsiHddSerialNumber отправляет команду INQUIRY на устройство, которая возвращает важные данные продукта (VPD), включая серийный номер устройства.
Если VPD успешно получен, программа извлекает серийный номер из буфера и отображает его пользователю.
Программа использует следующие SCSI-запросы:
SCSIOP_INQUIRY (0x12): Получает информацию VPD о устройстве, включая его серийный номер.
CDB_Inquiry_EVPD (0x01): Определяет, что команда inquiry должна возвращать EVPD-информацию.
Программа также использует следующие функции Windows API:
CreateFile: Открывает файл или handle устройства.
DeviceIoControl: Отправляет код I/O на устройство.
CloseHandle: Закрывает handle устройства.
Чтобы запустить эту программу, скомпилируйте ее с Delphi и выполните ее из командной строки, передав имя устройства, которое вы хотите запросить, как параметр (например, "PhysicalDrive0"). Программа отобразит серийный номер устройства, если она будет успешна.
Как получить информацию о SCSI дисках: можно использовать программу ScsiSN, которая позволяет отобразить серийный номер SCSI-харддиска.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS