1

I want to read the SMART-attributes of an USB-attached HDD via Powershell. Calling DeviceIOControl works fine if the HDD is build-in, but I dont understand the correct logics for getting the same info via USB. Here is a code snippet I have so far to get the SMART-version, but at this point I dont know how to continue. Can someone please explain the right sequence that should follow?

cls
Remove-Variable * -ea 0
$ErrorActionPreference = 'stop'
#requires -runasadmin

$drvId = 0
$marshal = [Runtime.InteropServices.Marshal]
$getSmartVersion = '0x074080'

$kernel32 = Add-Type -Name 'kernel32' -Namespace 'Win32' -PassThru -MemberDefinition @"
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
    String lpFileName,
    UInt32 dwDesiredAccess,
    UInt32 dwShareMode,
    IntPtr lpSecurityAttributes,
    UInt32 dwCreationDisposition,
    UInt32 dwFlagsAndAttributes,
    IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
    IntPtr   hDevice,
    uint     oControlCode,
    IntPtr   InBuffer,
    uint     nInBufferSize,
    IntPtr   OutBuffer,
    uint     nOutBufferSize,
    ref uint pBytesReturned,
    IntPtr  Overlapped);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CloseHandle(IntPtr hObject);
"@

$handle = $kernel32::CreateFile("\\.\PhysicalDrive$DrvId", [uint32]'0xc0000000', 3, [System.IntPtr]::Zero, 3, 64, [System.IntPtr]::Zero);
if ([int]$handle -lt 1) {throw 'cannot get handle.'}

# struct for SMART-version:
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public struct GETVERSIONINPARAMS_EX {
   public Byte   bVersion;
   public Byte   bRevision;
   public Byte   bReserved;
   public Byte   bIDEDeviceMap;
   public UInt32 fCapabilities;
   public UInt32 dwDeviceMapEx;
   public UInt16 wIdentifier;
   public UInt16 wControllerId;
   public UInt64 dwReserved;
 };
"@

# inBuffer:
$ptrInBuffer  = [System.IntPtr]::Zero
$inBufferSize = 0

# outBuffer:
$smartVersionStruct = New-Object GETVERSIONINPARAMS_EX
$outBufferSize = $marshal::SizeOf($smartVersionStruct)
$ptrOutBuffer  = $marshal::AllocHGlobal($OutBufferSize)

$resultSize = 0
$ioControlCode = [uint32]$getSmartVersion
if ($kernel32::DeviceIoControl($handle, $ioControlCode, $ptrInBuffer, $inBufferSize, $ptrOutBuffer, $outBufferSize, [ref]$resultSize, [System.IntPtr]::Zero)) {
    $smartVersionStruct = $marshal::PtrToStructure($ptrOutBuffer, [type]'GETVERSIONINPARAMS_EX')
    $smartVersionStruct | ft -AutoSize
}
$null = $kernel32::CloseHandle($handle)

# now the same for an USB-connected SSD:
$mediaList = gwmi -namespace root\Microsoft\Windows\Storage -class MSFT_PhysicalDisk
$usbMedia  = $mediaList | ?{$_.BusType -eq 7}

$diskList = gwmi -namespace root\cimv2 –class Win32_DiskDrive
$usbDisk  = $diskList.where({$_.Index -eq $usbMedia.DeviceId})

$usbMapping = gwmi -query "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"
$mapping    = @($usbMapping).where({([wmi]$_.Dependent).PnPDeviceId -eq $usbDisk.PnPDeviceId})
$usbHost    = [wmi]$mapping.Antecedent

# what should come next?
# getting the 'Root-Hub-Name' or not?
# check if UAS/USAP (USB Attached SCSI Protocol) is supported?
# IoControlCode = SCSCI-PassThrough or SCSIPassThroughDirect (each with or without Buffer)?
Carsten
  • 1,612
  • 14
  • 21
  • I don't believe it's possible. "When you plug in a device through USB, it communicates with your system with the USB mass storage protocol. It simply doesn't support SMART..." – NeoTheNerd Dec 14 '21 at 09:15
  • 1
    that scenario works fine for CrystalDiskInfo and also for SmartMonTools. So it is possible. The source-code of SmartMonTools is vailable here https://www.smartmontools.org/static/doxygen/os__win32_8cpp_source.html but I dont get the righ sequence of tasks for the above USB-scenario. – Carsten Dec 15 '21 at 15:52
  • @Carsten Interesting. Did you have any success querying USB Smart registers? – TheLegendaryCopyCoder Feb 21 '22 at 09:20

0 Answers0