I've ported the scheme (from diskid32, the "SCSI back door method") which seems to work for the non-admin user case (at least on my XP Pro machine). See what you think.
Of course, have to say a huge thanks to Rings for the original code (contributions from dmoc & others too I think), and of course thanks to the diskid32 author Lynn McGuire for making the diskid32 code available.
Code: Select all
; retrieve harddisk's model, serial, firmware (Windows only, may only cover Win2k/XP)
; original author Rings (posted Tue Jun 17, 2003) - requires admin rights
; -> added non-admin access using alternative method using so-called
; -> "SCSI back door" method from diskid32.cpp (ported to PB4 by mskuma)
; IDE registers
Structure IDEREGS
bFeaturesReg.b ; Used for specifying SMART "commands".
bSectorCountReg.b ; IDE sector count register
bSectorNumberReg.b ; IDE sector number register
bCylLowReg.b ; IDE low order cylinder value
bCylHighReg.b ; IDE high order cylinder value
bDriveHeadReg.b ; IDE drive/head register
bCommandReg.b ; Actual IDE command.
bReserved.b ; reserved for future use. Must be zero.
EndStructure
; SENDCMDINPARAMS contains the input parameters for the send command To drive function
Structure SENDCMDINPARAMS
cBufferSize.l ; Buffer size in bytes
irDriveRegs.IDEREGS ; Structure with drive register values
bDriveNumber.b ; Physical drive number to send command To (0,1,2,3)
bReserved.b[3] ; Reserved for future expansion
dwReserved.l[4] ; For future use
bBuffer.b[1] ; Input buffer
EndStructure
Structure DRIVERSTATUS
bDriveError.b
bIDEStatus.b
bReserved.b[2]
dwReserved.l[2]
EndStructure
Structure SENDCMDOUTPARAMS
cBufferSize.l
DStatus.DRIVERSTATUS
bBuffer.b[512]
EndStructure
; following 2 structures relate to SCSI method
Structure SRB_IO_CONTROL
HeaderLength.l
Signature.s{8}
Timeout.l
ControlCode.l
ReturnCode.l
Length.l
EndStructure
; The following struct defines the interesting part of the IDENTIFY buffer
Structure IDSECTOR
wGenConfig.w
wNumCyls.w
wReserved.w
wNumHeads.w
wBytesPerTrack.w
wBytesPerSector.w
wSectorsPerTrack.w
wVendorUnique.w[3]
sSerialNumber.s{20}
wBufferType.w
wBufferSize.w
wECCSize.w
sFirmwareRev.s{8}
sModelNumber.s{40}
wMoreVendorUnique.w
wDoubleWordIO.w
wCapabilities.w
wReserved1.w
wPIOTiming.w
wDMATiming.w
wBS.w
wNumCurrentCyls.w
wNumCurrentHeads.w
wNumCurrentSectorsPerTrack.w
ulCurrentSectorCapacity.l
wMultSectorStuff.w
ulTotalAddressableSectors.l
wSingleWordDMA.w
wMultiWordDMA.w
bReserved.b[128]
EndStructure
#DFP_RECEIVE_DRIVE_DATA = $7C088
bin.SENDCMDINPARAMS
bout.SENDCMDOUTPARAMS
Procedure.s ChangeHighLowByte(Instring.s)
;Change BIG-Little Endian
sdummy.s=""
L=Len(Instring)
For I=1 To L Step 2
If (I+1)<=L
sdummy.s=sdummy.s + Mid(Instring,I+1,1)+Mid(Instring,I,1)
EndIf
Next I
ProcedureReturn sdummy.s
EndProcedure
; try the original method
mvarCurrentDrive=0 ;If you have more hard-disks change it here
hdh = CreateFile_("\\.\PhysicalDrive" + Str(mvarCurrentDrive),#GENERIC_READ | #GENERIC_WRITE, #FILE_SHARE_READ | #FILE_SHARE_WRITE,0, #OPEN_EXISTING, 0, 0)
If hdh <> #INVALID_HANDLE_VALUE
bin\bDriveNumber = mvarCurrentDrive
bin\cBufferSize = 512
If (mvarCurrentDrive & 1)
bin\irDriveRegs\bDriveHeadReg = $B0
Else
bin\irDriveRegs\bDriveHeadReg = $A0
EndIf
bin\irDriveRegs\bCommandReg = $EC
bin\irDriveRegs\bSectorCountReg = 1
bin\irDriveRegs\bSectorNumberReg = 1
br=0
Result=DeviceIoControl_( hdh, #DFP_RECEIVE_DRIVE_DATA, bin, SizeOf(SENDCMDINPARAMS), bout, SizeOf(SENDCMDOUTPARAMS), @br, 0)
If br>0
hddfr = 55:hddln = 40 :
Modell.s=ChangeHighLowByte(PeekS(@bout\bBuffer[0]+hddfr-1 ,hddln ) )
hddfr = 21:hddln = 20 :
Serial.s=Trim(ChangeHighLowByte(PeekS(@bout\bBuffer[0]+hddfr-1 ,hddln ) ))
hddfr = 47:hddln = 8
Firmware.s=ChangeHighLowByte(PeekS(@bout\bBuffer[0]+hddfr-1 ,hddln ) )
MessageRequester("PhysicalDrive: Info about your harddisk","vendor(Modell)="+Modell.s + Chr(13) +"serial="+Serial.s+ Chr(13)+"Firmwareversion="+Firmware ,0)
EndIf
Else
Beep_(100,100) ; heard this = failed
EndIf
; ----------------------------------------------------------------------------------
; *** try SCSI back door approach *** (should be all that's needed, YMMV)
; ported from "int ReadIdeDriveAsScsiDriveInNT (void)" routine in diskid32.cpp (freeware)
; http://www.winsim.com/diskid32/diskid32.html
; valid values for the bCommandReg member of IDEREGS
#IDE_ATAPI_IDENTIFY = $A1 ; Returns ID sector For ATAPI.
#IDE_ATA_IDENTIFY = $EC ; Returns ID sector For ATA.
#FILE_DEVICE_SCSI = $0000001b
#IOCTL_SCSI_MINIPORT_IDENTIFY = ((#FILE_DEVICE_SCSI << 16) + $0501)
#IOCTL_SCSI_MINIPORT = $0004D008
#IDENTIFY_BUFFER_SIZE = 512
#SENDIDLENGTH = SizeOf(SENDCMDOUTPARAMS) + #IDENTIFY_BUFFER_SIZE
; start
bin2.SENDCMDINPARAMS
bout2.SENDCMDOUTPARAMS
For controller = 0 To 16
hScsiDriveIOCTL = CreateFile_("\\.\Scsi" + Str(controller) + ":",#GENERIC_READ | #GENERIC_WRITE, #FILE_SHARE_READ | #FILE_SHARE_WRITE,0, #OPEN_EXISTING, 0, 0)
If hScsiDriveIOCTL <> #INVALID_HANDLE_VALUE
drive.l = 0
For drive = 0 To 2
buffer = AllocateMemory(SizeOf(SRB_IO_CONTROL) + #SENDIDLENGTH)
dummy.l = 0
p.SRB_IO_CONTROL
p\HeaderLength = SizeOf(SRB_IO_CONTROL)
p\Timeout = 10000
p\Length = #SENDIDLENGTH
p\ControlCode = #IOCTL_SCSI_MINIPORT_IDENTIFY
p\Signature = "SCSIDISK"
bin2\irDriveRegs\bCommandReg = #IDE_ATA_IDENTIFY
bin2\bDriveNumber = drive
CopyMemory(@p, buffer, SizeOf(SRB_IO_CONTROL))
CopyMemory(@bin2, buffer + SizeOf(SRB_IO_CONTROL), SizeOf(bin2))
If (DeviceIoControl_(hScsiDriveIOCTL, #IOCTL_SCSI_MINIPORT, buffer, SizeOf(SRB_IO_CONTROL) + SizeOf(SENDCMDINPARAMS) - 1, buffer, SizeOf(SRB_IO_CONTROL) + #SENDIDLENGTH, @dummy, #Null))
CopyMemory(buffer + SizeOf(SRB_IO_CONTROL), @bout2, SizeOf(SENDCMDOUTPARAMS))
pId.IDSECTOR
CopyMemory(@bout2\bBuffer, @pId, SizeOf(IDSECTOR))
modelNumber.s = ChangeHighLowByte(pId\sModelNumber)
serialNumber.s = Trim(ChangeHighLowByte(pId\sSerialNumber))
firmware.s = ChangeHighLowByte(pId\sFirmwareRev)
str.s = "model = " + modelNumber + Chr(13) + "serial = " + serialNumber + Chr(13) + "firmware = " + firmware
MessageRequester("HD check using SCSI " + Str(controller), str)
EndIf
Next drive
EndIf
Next controller