因要用到硬盘的物理序列号,RAD10.4+win11,大家参考:
function TFrmMain.GetIdeSerialNumber() : PChar; stdcall;
const
IDENTIFY_BUFFER_SIZE = 512;
type
TIDERegs = packed record
bFeaturesReg: BYTE; // Used for specifying SMART "commands".
bSectorCountReg: BYTE; // IDE sector count register
bSectorNumberReg: BYTE; // IDE sector number register
bCylLowReg: BYTE; // IDE low order cylinder value
bCylHighReg: BYTE; // IDE high order cylinder value
bDriveHeadReg: BYTE; // IDE drive/head register
bCommandReg: BYTE; // Actual IDE command.
bReserved: BYTE; // reserved for future use. Must be zero.
end;
TSendCmdInParams = packed record
// Buffer size in bytes
cBufferSize: DWORD;
// Structure with drive register values.
irDriveRegs: TIDERegs;
// Physical drive number to send command to (0,1,2,3).
bDriveNumber: BYTE;
bReserved: array[0..2] of Byte;
dwReserved: array[0..3] of DWORD;
bBuffer: array[0..0] of Byte; // Input buffer.
end;
TIdSector = packed record
wGenConfig: Word;
wNumCyls: Word;
wReserved: Word;
wNumHeads: Word;
wBytesPerTrack: Word;
wBytesPerSector: Word;
wSectorsPerTrack: Word;
wVendorUnique: array[0..2] of Word;
sSerialNumber: array[0..19] of CHAR;
wBufferType: Word;
wBufferSize: Word;
wECCSize: Word;
sFirmwareRev: array[0..7] of Char;
sModelNumber: array[0..39] of Char;
wMoreVendorUnique: Word;
wDoubleWordIO: Word;
wCapabilities: Word;
wReserved1: Word;
wPIOTiming: Word;
wDMATiming: Word;
wBS: Word;
wNumCurrentCyls: Word;
wNumCurrentHeads: Word;
wNumCurrentSectorsPerTrack: Word;
ulCurrentSectorCapacity: DWORD;
wMultSectorStuff: Word;
ulTotalAddressableSectors: DWORD;
wSingleWordDMA: Word;
wMultiWordDMA: Word;
bReserved: array[0..127] of BYTE;
end;
PIdSector = ^TIdSector;
TDriverStatus = packed record
// 驱动器返回的错误代码,无错则返回0
bDriverError: Byte;
// IDE出错寄存器的内容,只有当bDriverError 为 SMART_IDE_ERROR 时有效
bIDEStatus: Byte;
bReserved: array[0..1] of Byte;
dwReserved: array[0..1] of DWORD;
end;
TSendCmdOutParams = packed record
// bBuffer的大小
cBufferSize: DWORD;
// 驱动器状态
DriverStatus: TDriverStatus;
// 用于保存从驱动器读出的数据的缓冲区,实际长度由cBufferSize决定
bBuffer: array[0..0] of BYTE;
end;
var
hDevice : THandle;
cbBytesReturned : DWORD;
SCIP : TSendCmdInParams;
aIdOutCmd : array[0..(SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1) - 1] of Byte;
IdOutCmd : TSendCmdOutParams absolute aIdOutCmd;
procedure ChangeByteOrder(var Data; Size: Integer);
var
ptr : PChar;
i : Integer;
c : Char;
begin
ptr := @Data;
for I := 0 to (Size shr 1) - 1 do
begin
c := ptr^;
ptr^ := (ptr + 1)^;
(ptr + 1)^ := c;
Inc(ptr, 2);
end;
end;
begin
Result := ''; // 如果出错则返回空串
if SysUtils.Win32Platform = VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
begin
// 提示! 改变名称可适用于其它驱动器,如第二个驱动器: '//./PhysicalDrive1/'
hDevice := CreateFile('//./PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
end
else // Version Windows 95 OSR2, Windows 98
hDevice := CreateFile('//./SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then Exit;
try
FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
cbBytesReturned := 0;
// Set up data structures for IDENTIFY command.
with SCIP do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
// bDriveNumber := 0;
with irDriveRegs do
begin
bSectorCountReg := 1;
bSectorNumberReg := 1;
// if Win32Platform=VER_PLATFORM_WIN32_NT then bDriveHeadReg := $A0
// else bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4);
bDriveHeadReg := $A0;
bCommandReg := $EC;
end;
end;
if not DeviceIoControl(hDevice, $0007C088, @SCIP, SizeOf(TSendCmdInParams) - 1,
@aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then Exit;
finally
CloseHandle(hDevice);
end;
with PIdSector(@IdOutCmd.bBuffer)^ do
begin
ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
(Pchar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
Result := Pchar(@sSerialNumber);
end;
end;
注意代码中高亮部分代码
SysUtils.Win32Platform = VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
在delphi老版本中可以使用SysUtils.Win32Platform调用;在新版本会出错。
而在delphi XE以上版本 SysUtils 要改为System.SysUtils。
另外可在主窗体程序中加入Timer,设置时间用作注册软件读取硬盘序列号后方可使用代码如下
procedure TfrmMain.tmr2Timer(Sender: TObject);
var
path:string;
begin
//path:=extractfilepath(application.ExeName)+'系统文件夹\ACCESS数据库名.mdb';
//dm.qry.ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+path+';Persist Security Info=False';
DM.qry.SQL.Clear;
dm.qry.SQL.Add('select * from sn where id=:id_a');
DM.qry.Parameters.ParamByName('id_a').value:='RFC'+Trim(GetIdeSerialNumber())+'JNGF';
DM.qry.Active:=True;
if DM.qry.RecordCount<=0 then
begin
if MessageDlg('您使用的软件未注册,请联系管理员注册后再使用!' + #13#10#13#10 +
' 本次程序终止使用!!!', mtError, [mbYes, mbNo], 0) = mrYes
then
begin
end;
Application.Terminate;
以下是网上找的代码示例大家参考:获得硬盘的ID序列号(XE10.1+WIN8.1)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Vcl.Imaging.jpeg;
type
TForm1 = class(TForm)
Panel1: TPanel;
Memo1: TMemo;
Button1: TButton;
Label1: TLabel;
Image1: TImage;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function GetScsiSerialNumber(const i: smallint): string;
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;
var
dwReturned: DWORD;
len: DWORD;
Buffer: array[0..SizeOf(TScsiPassThroughWithBuffers) + SizeOf(TScsiPassThrough) - 1] of Byte;
sptwb: TScsiPassThroughWithBuffers absolute Buffer;
hDevice: thandle;
begin
Result := '';
if SysUtils.win32Platform = VER_PLATFORM_WIN32_NT then
begin
if i = 0 then
hDevice := CreateFile('//./PhysicalDrive0',
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0)
else
hDevice := CreateFile('//./PhysicalDrive1',
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0);
end
else exit;
if hDevice = invalid_handle_value then exit;
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(hDevice, $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;
function GetIdeSerialNumber: pchar;
const IDENTIFY_BUFFER_SIZE = 512;
type
TIDERegs = packed record
bFeaturesReg: BYTE;
bSectorCountReg: BYTE;
bSectorNumberReg: BYTE;
bCylLowReg: BYTE;
bCylHighReg: BYTE;
bDriveHeadReg: BYTE;
bCommandReg: BYTE;
bReserved: BYTE;
end;
TSendCmdInParams = packed record
cBufferSize: DWORD;
irDriveRegs: TIDERegs;
bDriveNumber: BYTE;
bReserved: array[0..2] of Byte;
dwReserved: array[0..3] of DWORD;
bBuffer: array[0..0] of Byte;
end;
TIdSector = packed record
wGenConfig: Word;
wNumCyls: Word;
wReserved: Word;
wNumHeads: Word;
wBytesPerTrack: Word;
wBytesPerSector: Word;
wSectorsPerTrack: Word;
wVendorUnique: array[0..2] of Word;
sSerialNumber: array[0..19] of CHAR;
wBufferType: Word;
wBufferSize: Word;
wECCSize: Word;
sFirmwareRev: array[0..7] of Char;
sModelNumber: array[0..39] of Char;
wMoreVendorUnique: Word;
wDoubleWordIO: Word;
wCapabilities: Word;
wReserved1: Word;
wPIOTiming: Word;
wDMATiming: Word;
wBS: Word;
wNumCurrentCyls: Word;
wNumCurrentHeads: Word;
wNumCurrentSectorsPerTrack: Word;
ulCurrentSectorCapacity: DWORD;
wMultSectorStuff: Word;
ulTotalAddressableSectors: DWORD;
wSingleWordDMA: Word;
wMultiWordDMA: Word;
bReserved: array[0..127] of BYTE;
end;
PIdSector = ^TIdSector;
TDriverStatus = packed record
bDriverError: Byte;
bIDEStatus: Byte;
bReserved: array[0..1] of Byte;
dwReserved: array[0..1] of DWORD;
end;
TSendCmdOutParams = packed record
cBufferSize: DWORD;
DriverStatus: TDriverStatus;
bBuffer: array[0..0] of BYTE;
end;
procedure ChangeByteOrder(var Data; Size: Integer);
var
ptr: Pchar;
i: Integer;
c: Char;
begin
ptr := @Data;
for I := 0 to (Size shr 1) - 1 do begin
c := ptr^;
ptr^ := (ptr + 1)^;
(ptr + 1)^ := c;
Inc(ptr, 2);
end;
end;
var
hDevice: Thandle;
cbBytesReturned: DWORD;
SCIP: TSendCmdInParams;
aIdOutCmd: array[0..(SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1) - 1] of Byte;
IdOutCmd: TSendCmdOutParams absolute aIdOutCmd;
begin
Result := '';
if SysUtils.Win32Platform = VER_PLATFORM_WIN32_NT then
// Windows NT, Windows 2000
hDevice := CreateFile('//./PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0)
else
// Version Windows 95 OSR2, Windows 98
hDevice := CreateFile('//./SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then Exit;
try
FillChar(SCIP, SizeOf(TSendCmdInParams) - 1, #0);
FillChar(aIdOutCmd, SizeOf(aIdOutCmd), #0);
cbBytesReturned := 0;
with SCIP do begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
with irDriveRegs do begin
bSectorCountReg := 1;
bSectorNumberReg := 1;
bDriveHeadReg := $A0;
bCommandReg := $EC;
end;
end;
if not DeviceIoControl(hDevice, $0007C088, @SCIP, SizeOf(TSendCmdInParams) - 1,
@aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil) then Exit;
finally
CloseHandle(hDevice);
end;
with PIdSector(@IdOutCmd.bBuffer)^ do begin
ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
(Pchar(@sSerialNumber) + SizeOf(sSerialNumber))^ := #0;
Result := Pchar(@sSerialNumber);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
stmp:String;
begin
//记得右击以管理员运行
stmp := StrPas(PAnsiChar(GetIdeSerialNumber));
if stmp<>'' then
begin
Memo1.Lines.Add('无参:' + stmp);
end
else
begin
stmp := Trim(GetScsiSerialNumber(0));
Memo1.Lines.Add('有参:' + stmp);
end;
end;
end.
注意事项:
1.记得右击以管理员运行。
2.SysUtils 在XE中要改为System.SysUtils。