1

I have 2 drives in my computer and 1 drive attached via USB. When I use this code I correctly get 3 devices:

function TForm1.GetHDDCount: integer;
var f, i: integer;
begin
  Result := 0;

  for i:=0 to 99 do begin
    f := FileOpen('\\.\PhysicalDrive' + IntToStr(i), fmOpenRead or fmShareDenyNone);
    Result := i;
    if f = -1 then break;
    FileClose(f);
  end;

end;

However then I use this code and I only get information about first 2 disks (not the one on USB):

var Disks: Integer;
    i: Integer;
begin
  Disks := GetHDDCount;

  ReadSmart;

  for i:=0 to Disks do begin
    ListBox1.Items.Add( HDDInfo[i].Model );
  end;

Code of smart.pas:

unit smart;

interface

  uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

procedure OpenSMART;
procedure ReadSMART;
procedure CloseSMART;
function  GetATTRName(attr:byte;var comm:string):string;
function GetFlags(flags:word):string;
procedure SwapBytes( var buf; count : integer );

Type
  TSixByte = array[0..5] of byte;

  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;
        hz           : array[1..72] of byte;
        LBA48        : array[0..5] of byte;
    bReserved:array[0..127] of byte;
        end;

    TGETVERSIONOUTPARAMS = packed record 
      version      : byte; 
      revision     : byte; 
      reserved     : byte;
      IDEDevMap    : byte; 
      Capabilities : dword; 
      reserved1    : dword; 
      reserved2    : dword; 
      reserved3    : dword; 
      reserved4    : dword; 
    End; 


    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;
    end;                // reserved for future use.  Must be zero.


 TSENDCMDINPARAMS = packed record
     cBufferSize:dword;     // Buffer size in bytes
       irDriveRegs:TIDERegs;        // Structure with drive register values.
       bDriveNumber:byte;       // Physical drive number to send
                            // command to (0,1,2,3).
       bReserved: array[0..3] of byte;      // Reserved for future expansion.
       dwReserved: array [0..4] of dword;       // For future use.
       bBuffer:array[0..1024*8] of byte;            // Input buffer.
    end;


 TDRIVERSTATUS = packed record
    bDriverError:byte;      // Error code from driver,
                    // or 0 if no error.
    bIDEStatus:byte;        // Contents of IDE Error register.
                    // Only valid when bDriverError
                    // is SMART_IDE_ERROR.
    bReserved:array[0..3] of byte;  // Reserved for future expansion.
    dwReserved:array[0..1] of dword;// Reserved for future expansion.
  end;

   TDRIVEATTRIBUTE = packed record
        bAttrID:byte;       // Identifies which attribute
        wStatusFlags:word;  // see bit definitions below
        Value:byte;     // Current normalized value
        Worst:byte; // How bad has it ever been?
          Raw:TSixByte; // Un-normalized value
        bReserved:byte;     // ...
    end;

   TATTRTHRESHOLD = packed record
        bAttrID    : byte;
        bWarrantyThreshold : byte;
        reserved   : array[0..9] of byte;
    end;

 TSENDCMDOUTPARAMS = packed record
    cBufferSize  : DWord;       // Size of bBuffer in bytes
    DriverStatus : TDriverStatus;       // Driver status structure.
    attr         : array[0..255] of TDRIVEATTRIBUTE;            // Buffer of arbitrary length in which to store the data read from the                                          // drive.
    end;

   TSMARTATTRNAME = record
    value : word;
    name  : string[40];
    comm  : string[160];
   end;

   THDDInfo = record
    Model    : string;
    FW       : string;
    SN       : string;
    LBABits  : integer;
    LBASize  : int64;
    Cache    : integer;
   End;

var
  VersParams  : TGETVERSIONOUTPARAMS;
  SCOP        : array[0..3] of TSENDCMDOUTPARAMS;
  AttrCnt     : array[0..3] of word;
  hSMARTIOCTL : array[0..3] of thandle;
  IDSECTOR    : array[0..3] of TIDSECTOR;
  Thresholds  : array[0..3,0..255] of TATTRTHRESHOLD;
  HDDInfo     : array[0..3] of THDDInfo;

const
  PRE_FAILURE_WARRANTY       =     $01;
  ON_LINE_COLLECTION         =     $02;
  PERFORMANCE_ATTRIBUTE      =     $04;
  ERROR_RATE_ATTRIBUTE       =     $08;
  EVENT_COUNT_ATTRIBUTE      =     $10;
  SELF_PRESERVING_ATTRIBUTE  =     $20;
  IDENTIFY_BUFFER_SIZE       =     512;
  READ_THRESHOLD_BUFFER_SIZE =     512;

  SMART_READ_ATTRIBUTE_THRESHOLDS = $D1;

  IDE_ATAPI_ID           =     $A1; // Returns ID sector for ATAPI.
  IDE_ID_FUNCTION        =     $EC; // Returns ID sector for ATA.


var
   SCIP    : TSENDCMDINPARAMS;

const
   DFP_GET_VERSION        = $00074080;
   DFP_RECEIVE_DRIVE_DATA = $0007c088;
   DFP_SEND_DRIVE_COMMAND = $0007c084;
   SMART_ENABLE_SMART_OPERATIONS  = $D8;
   SMART_DISABLE_SMART_OPERATIONS = $D9;
   SMART_RETURN_SMART_STATUS      = $DA;
   SMART_CYL_LOW                  = $4F;
   SMART_CYL_HI                   = $C2;
   IDE_EXECUTE_SMART_FUNCTION     = $B0;
   READ_ATTRIBUTE_BUFFER_SIZE     = 512;
   SMART_READ_ATTRIBUTE_VALUES    = $D0;    // ATA4: Renamed

   AttrCount = 45;
   SmartAttrNames : array[1..AttrCount] of TSMARTAttrName=
   (
   (Value:1;Name:'Raw Read Error Rate';Comm:'Frequency of errors appearance while reading RAW data from a disk'),
   (Value:2;Name:'Throughput Performance';Comm:'The average efficiency of hard disk'),
   (Value:3;Name:'Spin Up Time';Comm:'Time needed by spindle to spin-up'),
   (Value:4;Name:'Start/Stop Count';Comm:'Number of start/stop cycles of spindle'),
   (Value:5;Name:'Reallocated Sector Count';Comm:'Quantity of remapped sectors'),
   (Value:6;Name:'Read Channel Margin';Comm:'Reserve of channel while reading'),
   (Value:7;Name:'Seek Error Rate';Comm:'Frequency of errors appearance while positioning'),
   (Value:8;Name:'Seek Time Performance';Comm:'The average efficiency of operations while positioning'),
   (Value:9;Name:'Power-On Hours Count';Comm:'Quantity of elapsed hours in the switched-on state'),
   (Value:10;Name:'Spin-up Retry Count';Comm:'Number of attempts to start a spindle of a disk'),
   (Value:11;Name:'Calibration Retry Count';Comm:'Number of attempts to calibrate a drive'),
   (Value:12;Name:'Power Cycle Count';Comm:'Number of complete start/stop cycles of hard disk'),
   (Value:13;Name:'Soft Read Error Rate';Comm:'Frequency of "program" errors appearance while reading data from a disk'),
   (Value:191;Name:'G-Sense Error Rate';Comm:'Frequency of mistakes appearance as a result of impact loads'),
   (Value:192;Name:'Power-Off Retract Cycle';Comm:'Number of the fixed "turning off" drive cycles (Fujitsu: Emergency Retract Cycle Count)'),
   (Value:193;Name:'Load/Unload Cycle Count';Comm:'Number of cycles into Landing Zone position'),
   (Value:194;Name:'HDD Temperature';Comm:'Temperature of a Hard Disk Assembly'),
   (Value:195;Name:'Hardware ECC Recovered';Comm:'Frequency of the on the fly errors (Fujitsu: ECC On The Fly Count)'),
   (Value:196;Name:'Reallocated Event Count';Comm:'Quantity of remapping operations'),
   (Value:197;Name:'Current Pending Sector Count';Comm:'Current quantity of unstable sectors (waiting for remapping)'),
   (Value:198;Name:'Off-line Scan Uncorrectable Count';Comm:'Quantity of uncorrected errors'),
   (Value:199;Name:'UltraDMA CRC Error Rate';Comm:'Total quantity of errors CRC during UltraDMA mode'),
   (Value:200;Name:'Write Error Rate';Comm:'Frequency of errors appearance while recording data into disk (Western Digital: Multi Zone Error Rate)'),
   (Value:201;Name:'Soft Read Error Rate';Comm:'Frequency of the off track errors (Maxtor: Off Track Errors)'),
   (Value:202;Name:'Data Address Mark Errors';Comm:'Frequency of the Data Address Mark errors'),
   (Value:203;Name:'Run Out Cancel';Comm:'Frequency of the ECC errors (Maxtor: ECC Errors)'),
   (Value:204;Name:'Soft ECC Correction';Comm:'Quantity of errors corrected by software ECC'),
   (Value:205;Name:'Thermal Asperity Rate';Comm:'Frequency of the thermal asperity errors'),
   (Value:206;Name:'Flying Height';Comm:'The height of the disk heads above the disk surface'),
   (Value:207;Name:'Spin High Current';Comm:'Quantity of used high current to spin up drive'),
   (Value:208;Name:'Spin Buzz';Comm:'Quantity of used buzz routines to spin up drive'),
   (Value:209;Name:'Offline Seek Performance';Comm:'Drives seek performance during offline operations'),
   (Value:220;Name:'Disk Shift';Comm:'Shift of disk is possible as a result of strong shock loading in the store, as a result of it`s falling or for other reasons'),
   (Value:221;Name:'G-Sense Error Rate';Comm:'This attribute is an indication of shock-sensitive sensor - total quantity of errors appearance as a result of impact loads '),
   (Value:222;Name:'Loaded Hours';Comm:'Loading on drive caused by the general operating time of hours it stores'),
   (Value:223;Name:'Load/Unload Retry Count';Comm:'Loading on drive caused by numerous recurrences of operations like: reading, recording, positioning of heads, etc.'),
   (Value:224;Name:'Load Friction';Comm:'Loading on drive caused by friction in mechanical parts of the store'),
   (Value:225;Name:'Load/Unload Cycle Count';Comm:'Total of cycles of loading on drive'),
   (Value:226;Name:'Load-in Time';Comm:'General time of loading for drive'),
   (Value:227;Name:'Torque Amplification Count';Comm:'Quantity efforts of the rotating moment of a drive'),
   (Value:228;Name:'Power-Off Retract Count';Comm:'Quantity of the fixed turning off`s a drive'),
   (Value:230;Name:'GMR Head Amplitude';Comm:'Amplitude of heads trembling (GMR-head) in running mode'),
   (Value:231;Name:'Temperature';Comm:'Temperature of a drive'),
   (Value:240;Name:'Head Flying Hours';Comm:'Time while head is positioning'),
   (Value:250;Name:'Read Error Retry Rate';Comm:'Frequency of errors appearance while reading data from a disk')
   );

   flagnames : array[0..5] of string[2] = ('PF','OC','PA','ER','EC','SP');
implementation

{------------------------------------------------------------------}
function GetFlags(flags:word):string;
var
  i : integer;
  s : string;

Begin
 s := '';
  for i := 0 to 5 do
    if flags and (1 shl i) <> 0 then
      s := s + flagnames[i]+#32;
 GetFlags := s;     
End;

{------------------------------------------------------------------}
function  GetATTRName(attr:byte;var comm:string):string;
var
  i : integer;
begin
  for i := 1 to attrCount do
   begin
    if attr = SmartAttrNames[i].Value then
     begin
      GetAttrName := SmartAttrNames[i].name;
      Comm := SmartAttrNames[i].comm;
      exit;
     end;
   end;
 GetAttrName := 'Unknown';
end;

{------------------------------------------------------------------}
procedure SwapBytes( var buf; count : integer );
Assembler;
Asm
        pushad
        mov     esi, buf
        mov     edi, esi
        mov     ecx, count
        shr     ecx, 1
        test    ecx, ecx
        jz      @@exit
        @@rep:
        lodsw
        xchg    ah, al
        stosw
        dec     ecx
        jnz     @@rep
@@exit:
        popad
End;

{------------------------------------------------------------------}
procedure OpenSMART;
var
  i       : integer;
  c       : dword;
  s, s1, s2   : string;
  os          : TOSVERSIONINFO;
begin
 for i := 0 to 3 do
  begin

 os.dwPlatformId := 0;
 os.dwOSVersionInfoSize := sizeof(OSVERSIONINFO);
 GetVersionEx( os );

    hSMARTIOCTL[i] := 0;

   // hSMARTIOCTL[i] := CreateFile(pchar( '\\.\PHYSICALDRIVE2'),

     hSMARTIOCTL[i] := CreateFile(pchar( '\\.\PhysicalDrive'+format('%d',[i])),
   //  hSMARTIOCTL[i] := CreateFile(pchar( '\\.\C:'),
                    GENERIC_READ or GENERIC_WRITE,
                    FILE_SHARE_READ or FILE_SHARE_WRITE,
                    NIL,
                    OPEN_EXISTING,
                    0,
                    0);
    


     if hSMARTIOCTL[i] = INVALID_HANDLE_VALUE then continue;



     DeviceIoControl(hSMARTIOCTL[i], DFP_GET_VERSION,
           NIL,
           0,
           @VersParams,
           sizeof(VersParams),
           c, NIL) ;

        FillChar( SCIP, SizeOf( SCIP ), 0 );
    SCIP.cBufferSize := 0;

    SCIP.irDriveRegs.bFeaturesReg := SMART_ENABLE_SMART_OPERATIONS;
    SCIP.irDriveRegs.bSectorCountReg := 1;
    SCIP.irDriveRegs.bSectorNumberReg := 1;
    SCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
    SCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;

    //
    // Compute the drive number.
    //
    SCIP.irDriveRegs.bDriveHeadReg := $A0 or (( i and 1) shl 4);
    SCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
    SCIP.bDriveNumber := i;



        DeviceIoControl(hSMARTIOCTL[i], DFP_SEND_DRIVE_COMMAND,
                @SCIP, sizeof(TSENDCMDINPARAMS) - 1,
                @SCOP[i], sizeof(TSENDCMDOUTPARAMS) - 1,
                c, NIL);

    SCIP.cBufferSize := IDENTIFY_BUFFER_SIZE;
    SCIP.irDriveRegs.bFeaturesReg := 0;
    SCIP.irDriveRegs.bSectorCountReg := 1;
    SCIP.irDriveRegs.bSectorNumberReg := 1;
    SCIP.irDriveRegs.bCylLowReg := 0;
    SCIP.irDriveRegs.bCylHighReg := 0;

    SCIP.irDriveRegs.bDriveHeadReg := $A0 or ((i and 1) shl 4);
    SCIP.irDriveRegs.bCommandReg := IDE_ID_FUNCTION; {  or IDE_ATAPI_ID;}
    SCIP.bDriveNumber := i;
    SCIP.cBufferSize := IDENTIFY_BUFFER_SIZE;

        DeviceIoControl(hSMARTIOCTL[i], DFP_RECEIVE_DRIVE_DATA,
        @SCIP, sizeof(TSENDCMDINPARAMS) - 1,
        @SCOP[i], sizeof(TSENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1,
        c, NIL) ;

        Move(SCOP[i].attr, IDSECTOR[i], SizeOf( IDSECTOR[i] ) );

        SCIP.cBufferSize := READ_THRESHOLD_BUFFER_SIZE;

        SCIP.irDriveRegs.bFeaturesReg := SMART_READ_ATTRIBUTE_THRESHOLDS;
        SCIP.irDriveRegs.bSectorCountReg := 1;
        SCIP.irDriveRegs.bSectorNumberReg := 1;
        SCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
        SCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;

        SCIP.irDriveRegs.bDriveHeadReg := $A0 or ((i and 1) shl 4);
        SCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
        SCIP.bDriveNumber := i;

        DeviceIoControl(hSMARTIOCTL[i], DFP_RECEIVE_DRIVE_DATA,
        @SCIP, sizeof(TSENDCMDINPARAMS) - 1,
        @SCOP[i], sizeof(TSENDCMDOUTPARAMS) + READ_THRESHOLD_BUFFER_SIZE - 1,
        c, NIL) ;

        Move( SCOP[ i ].attr, Thresholds[ i ], SizeOf( Thresholds[ i ] ) );

        HDDInfo[ i ].LBASize := 0;
        HDDInfo[ i ].LBABits := 28;

   if idsector[i].ulTotalAddressableSectors = $FFFFFFF then
      begin
       move( idsector[i].lba48,HDDInfo[ i ].LBASize,6);
       HDDInfo[ i ].LBABits := 48;
      end
         else
       move( idsector[i].ulTotalAddressableSectors,HDDInfo[ i ].LBASize,4);


       s := IDSECTOR[i].sModelNumber +#32;
       SwapBytes( s[1], Length(s) );

       While (Length(s)<>0) and (s[Length(s)-1]=#32) do
         SetLength( s, length(s)-1);

   HDDInfo[ i ].Model := s;

   s := IDSECTOR[i].sFirmwareRev +#32;
   SwapBytes( s[1], Length(s) );

   HDDInfo[ i ].FW := s;

   s := IDSECTOR[i].sSerialNumber+#32;
   SwapBytes( s[1], Length(s) );
    While (Length(s)<>0) and (s[1]=#32) do
     delete(s,1,1);

   HDDInfo[ i ].SN := s;
   HDDInfo[ i ].Cache := IDSECTOR[i].wBufferSIze div 2;

  end;
end;

{------------------------------------------------------------------}
procedure CloseSMART;
var
  i : integer;
Begin
 for i := 0 to 3 do
     CloseHandle(hSMARTIOCTL[i]);

End;

{------------------------------------------------------------------}
procedure ReadSMART;
var
  i,j     : integer;
  c       : dword;
Begin

OpenSMART;
 for i := 0 to 3 do
  begin
     if hSMARTIOCTL[i] = INVALID_HANDLE_VALUE then continue;


    SCIP.cBufferSize := READ_ATTRIBUTE_BUFFER_SIZE;
    SCIP.irDriveRegs.bFeaturesReg := SMART_READ_ATTRIBUTE_VALUES;
    SCIP.irDriveRegs.bSectorCountReg := 1;
    SCIP.irDriveRegs.bSectorNumberReg := 1;
    SCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
    SCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;

    //
    // Compute the drive number.
    //
    SCIP.irDriveRegs.bDriveHeadReg := $A0 or (( i and 1) shl 4);
    SCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
    SCIP.bDriveNumber := i;
        FillChar( SCOP[i], SizeOf( SCOP[i] ), 0 );

        DeviceIoControl(hSMARTIOCTL[i], DFP_RECEIVE_DRIVE_DATA,
                @SCIP, sizeof(TSENDCMDINPARAMS) - 1,
                @SCOP[ i ], sizeof(TSENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE,
                c, NIL);

     AttrCnt[ i ] := 0;
     for j := 0 to 255 do
      if SCOP[ i ].attr[ j ].bAttrID = 0 then
       begin
         AttrCnt[ i ] := j;
         break;
       end;
  end;
 CloseSMART;

End;
end.

I also used this code to make sure my USB drive is one of the "PhysicalDrives":

uses ActiveX, ComObj

procedure TForm1.Button5Click(Sender: TObject);
const wbemFlagForwardOnly = $00000020;
var FSWbemLocator : OLEVariant;
    FWMIService   : OLEVariant;
    FWbemObjectSet: OLEVariant;
    FWbemObject   : OleVariant;
    oEnum         : IEnumvariant;
    sValue        : string;
    outt          : LongWord;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;

  while oEnum.Next(1, FWbemObject, outt) = 0 do
  begin
    sValue:= FWbemObject.Properties_.Item('DeviceID').Value;
    Memo1.Lines.Add(Format('DeviceID       %s',[sValue]));// String
    sValue:= FWbemObject.Properties_.Item('Model').Value;
    Memo1.Lines.Add(Format('Model          %s',[sValue]));// String
    Memo1.Lines.Add('-----------------------------');

    FWbemObject:= Unassigned;
  end;
end;

And it returned:

DeviceID       \\.\PHYSICALDRIVE1
Model          ADATA SP550 ATA Device
-----------------------------
DeviceID       \\.\PHYSICALDRIVE0
Model          WDC WD40EZRZ-00GXCB0 ATA Device
-----------------------------
DeviceID       \\.\PHYSICALDRIVE2
Model          WD Elements SE 2622 USB Device
-----------------------------

How can I get S.M.A.R.T. information from USB disks?

Tom
  • 2,962
  • 3
  • 39
  • 69
  • 1
    Since `GetHDDCount()` is also zero based (actually returning count minus 1) your loop `for i:=0 to Disks-1` always ends too soon. Furthermore the drive numbers are not guaranteed to be gapless: having 3 drives and unmounting one doesn't mean #0 and #1 remain - it doesn't even mean mounting a new drive will become #3. Just like with disks. – AmigoJack Jan 28 '22 at 20:58
  • 1
    `GetHDDCount()` is coded completely wrong. It is leaking device handles, and it is not actually counting existing devices, only devices it can successfully open, it doesn't account for devices you might not have access to. There are more appropriate APIs for determining available devices. – Remy Lebeau Jan 28 '22 at 21:07
  • @AmigoJack Thank you, I updated the code. The problem still exists and the USB device is for sure \\.\PhysicalDrive2 – Tom Jan 29 '22 at 01:06
  • 1
    Can you retrieve S.M.A.R.T information from the USB drive using some other software like [HWiNFO](https://www.hwinfo.com/)? Not all USB drive enclosures allow passing S.M.A.R.T information through – SilverWarior Jan 29 '22 at 05:49
  • 1
    [CrystalDiskInfo](https://crystalmark.info/en/software/crystaldiskinfo/) is a free software to check for S.M.A.R.T. data on ATA disks - if it doesn't find/support your USB device then chances are it just doesn't have any. – AmigoJack Jan 29 '22 at 08:42
  • @SilverWarior Yes, I can get SMART data using other programs, like CrystalDisk Info. – Tom Jan 29 '22 at 10:58
  • 1
    After reading a bit on this topic I found out the reason of your problems here. According to [How do you get the physical drive number of a removable device](https://stackoverflow.com/a/5503112/3636228) your external hard drive isn't considered as Physical drive but instead as removable drive. And you can not access to the removable drives in the same way as you do to physical drives. – SilverWarior Jan 29 '22 at 14:21
  • @SilverWarior The thing is I checked with another code and my USB drive has been assigned "\\.\PhysicalDrive2" identifier. I updated my question with the code I used to show IDs. – Tom Jan 29 '22 at 14:53
  • 1
    There are a few variations of this code (Windows95) floating around but I suspect that the call DFP_GET_VERSION retrieves the Driver version for IDE drives only. The IDEDevMap was used in one demo to see how many drives where attached and had SMART enabled, for me only one ever showed. IDE=two channels and two devices per channel. I also have several USB drives and although the handle can be opened all of the Device function return: unsupported.. – FredS Jan 30 '22 at 17:02
  • @Tom Hi ! Did you finally find a method to read data from SMART for USB drives? – Marus Gradinaru Nov 08 '22 at 12:00
  • @MarusGradinaru No, I didn't – Tom Nov 08 '22 at 13:29

0 Answers0