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