;;
;;=====================================================================================-----
;;
;;FUNCTION IsSSD()
;;
;;ACTION Determines if a disk is SSD or Rotating Media
;;
;;AUTHOR Glenn Barnas
;;
;;VERSION 1.0 / 2014/12/05
;;
;;SYNTAX IsSSD(Drive [, MfrList] [,ReturnVSA])
;;
;;PARAMETERS Drive - REQUIRED - Drive letter to check
;;
;; MfrList - OPTIONAL - Array
;; - Array of manufacturer names/IDs that ONLY manufacture SSD drives
;; or controller chips.
;; (eg: 'Satron', 'JMicron', 'Indilinx', 'SandForce', 'STEC', 'Crucial')
;; Use of this parameter (using at least the above list) is highly
;; recommended.
;;
;; ReturnVSA - OPTIONAL - Integer
;; - A Boolean value that returns the Vendor Specific Attribute data
;; instead of a digit indicating the SSD detections that were triggered.
;; This can be used with any media type that supports SMART data.
;;
;;REMARKS A method to determine if a target drive is an SSD, usually to
;; decide on the type of maintenance (if any) to apply. It can
;; also return SMART data for additional health analysis.
;;
;;RETURNS A single digit ranging from 0-255 that represents the detections
;; that were triggered, or an array of Vendor Specific Attribute data.
;; The binary weighted detection values are:
;; 0 NOT an SSD - no detections were triggered
;; 1 SMART Attribute 0x03 (Spin Up Time) is Defined and value is Zero
;; 2 SMART Attribute 0x04 (Start/Stop Count) is Defined and value is Zero
;; 4 SMART Attribute 0x08 (Seek Time Performance) is Defined and value
;; is Zero
;; 8 (not currently implemented)
;; 16 One of the listed manufacturers was found in the Product ID string
;; 32 Text string "SSD" was detected in the Product ID string
;; 64 SMART Attribute unique to SSD drives was detected
;; 128 SMART Data was not retrived - detection is limited to Product ID
;; containing "SSD" or a match in the Manufacturer ID list.
;;
;; Values between 1 and 127 indicate that the drive is "probably" an
;; SSD type, with the value increasing with the likelyhood of a valid
;; detection. Values below 8 should be considered suspect, as not all
;; vendors report these values on SSD drives (although rotating media
;; drives that do report these values will most likely be >0).
;; A value of Zero indicates that no detections were triggered, so the
;; drive is almost certainly a rotating-media type.
;; Values of 128 or higher indicate that SMART data was not retrieved
;; from the drive; this prevents all of the low probability and the
;; highest probability detections from being triggered. A value of 128
;; is NOT an SSD, but values above 128 indicate that detections based
;; on Product String evaluations have been triggered. The user may want
;; to further test the product string.
;;
;; This method always return SUCCESS.
;;
;; When the argument ReturnVSA is true, an array in the following format
;; is returned that contains the Drive Product ID string and all data in
;; the SMART Vendor Specific Attribute.
;; * ProductID The Product ID string, usually containing the vendor
;; name and model number.
;; * Attr_Data A comma-delimited string in the format:
;; Attr_ID (hh),Cur_Val (d),Wst_Val (d),Thr_Val (d),RAW_Data (7 hh bytes)
;;
;; (hh) is a hex byte, (d) is a decimal value in the range 0-255
;; Attr_ID is the attribute ID; Cur_Val is the current value; Wst_Val
;; is the detected worst-case value; Thr_Val is the threshold value
;; (if used). All remaining data is returned as Hex bytes in the
;; RAW_Data field.
;;
;; As many Attr_Data rows are returned as Vendor-Specific Attributes
;; are defined in the SMART data query. This information can be used
;; to evaluate the health of any SMART-enabled drive.
;;
;; This method will return SUCCESS if SMART data is available, and FAIL
;; (Exit 1/single-element array with ProductID) if SMART data is not available.
;;
;; Will not likely work with USB-attached drives due to hardware limitations.
;;
;;
;;DEPENDENCIES WMI, SMART
;;
;;TESTED WITH W2K8, W2K12, Win7, Win8
;;
;;EXAMPLES
;
Function IsSSD($_Volume, Optional $_aMfrList, OPTIONAL $_ReturnVSA)
Dim $_aVolData[4] ; array of disk volume data
Dim $_WMIService ; WMI Service Object
Dim $_wmiDiskDrives, $_wmiDiskDrive ; WMI Drives collection & enumerator
Dim $_wmiDiskPartitions, $_wmiDiskPartition ; WMI Partitions collection & enumerator
Dim $_wmiLogicalDisks, $_wmiLogicalDisk ; WMI LogicalDisks collection & enumerator
Dim $_wmiSmartItems, $_wmiSmartItem ; WMI SMART data objects
Dim $_Query ; Query string
Dim $_aVSpec ; Array of VendorSpecific data
Dim $_O, $_I ; enumeration pointers
Dim $_VSAttr ; Vendor Specific Attribute ID to search for
Dim $_OutString ; Output String for when ReturnVSA is true
Dim $_ECode ; exit code variable
$IsSSD = 0
$_ECode = 0
; Need to match disk volume (D:) with the physical device to determine if it is an SSD drive
; instantiate a WMI connection
$_WMIService = GetObject('winmgmts:\\.\root\cimv2')
; Get all disk drive objects
$_wmiDiskDrives = $_WMIService.ExecQuery('Select DeviceID, Caption, PNPDeviceID from Win32_DiskDrive',,48)
For Each $_wmiDiskDrive in $_wmiDiskDrives
; create a query for this disk object
$_Query = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + $_wmiDiskDrive.DeviceID + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition"
; get the list of related objects for this disk & enum results
$_wmiDiskPartitions = $_WMIService.ExecQuery($_Query)
For Each $_wmiDiskPartition in $_wmiDiskPartitions
; Get the related Logical Disk data and enum results
$_wmiLogicalDisks = $_wmiService.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + $_wmiDiskPartition.DeviceID + "'} WHERE AssocClass = Win32_LogicalDiskToPartition")
For Each $_wmiLogicalDisk In $_wmiLogicalDisks
; If the Device ID matches the desired volume, proceed
If $_wmiLogicalDisk.DeviceID = $_Volume
If InStr($_wmiDiskDrive.Caption, 'SSD')
$IsSSD = $IsSSD + 32 ; "SSD" detected in product name
EndIf
; Check for manufacturers that only make SSD (no spinning) drives
For Each $_I in $_aMfrList
If InStr($_wmiDiskDrive.Caption, $_I)
$IsSSD = $IsSSD + 16
EndIf
Next
; Not ID'd in product name - additional examination required
$_aVolData[0] = $_wmiLogicalDisk.DeviceID
$_aVolData[1] = $_wmiDiskDrive.Caption
$_aVolData[2] = $_wmiDiskDrive.DeviceID
$_aVolData[3] = $_wmiDiskPartition.DeviceID
$_aVolData[4] = $_wmiDiskDrive.PNPDeviceID
EndIf
Next
Next
Next
$_WMIService = 0 ; clear the prior object
; check the Vendor Specific Attributes - each attribute is formatted as:
; 1 Byte - Identifier - the Attribute ID
; 1 Byte - Threshold - the failure limit for the attribute
; 1 Bytes - Status Flag - bitwise values, mostly vendor-specific (not returned)
; 1 Byte - Value - the current "health" measured against the Threshold
; 1 Byte - Worst - the worst value ever detected, measured against the Threshold
; 6 Bytes - Data - the raw measurement data
; 1 Bytes - Status Flag - bitwise values, mostly vendor-specific (not returned)
If $_ReturnVSA
$_OutString = $_aVolData[1] + ' / ' + $_aVolData[3] + @CRLF
EndIf
; extract the SMART data from the drive for further analysis
$_WMIService = GetObject('winmgmts:\\.\root\WMI')
$_wmiSmartItems = $_WMIService.ExecQuery('SELECT InstanceName,VendorSpecific FROM MSStorageDriver_ATAPISmartData')
For Each $_wmiSmartItem In $_wmiSmartItems
; does the instance name match that of the volume requested?
If InStr($_wmiSmartItem.InstanceName, $_aVolData[4])
$_aVSpec = Split(Join($_wmiSmartItem.VendorSpecific, ','), ',')
; Scan SSD-Only objects
For $_O = 2 to UBound($_aVSpec) Step 12
; Find the Vendor Specific Attribute ID as a 2-char Hex Byte
$_VSAttr = Right('0' + DecToHex($_aVSpec[$_O]), 2)
; Does it match any SSD-Specific attributes?
If InStr(':AB:B6:FC:', ':' + $_VSAttr + ':')
$IsSSD = $IsSSD + 64
EndIf
; other tests of object/value combinations can be done here (Start Count=0 or Seek Time=0)
; attribute 0x03 (Spin Up Time) with a value of 0
$_I = $_aVSpec[$_O + 3] ; Attribute Value
If $_VSAttr ='03' And $_I = 0
$IsSSD = $IsSSD + 1
EndIf
; attribute 0x04 (Start/Stop Count) with a value of 0
$_I = $_aVSpec[$_O + 3] ; Attribute Value
If $_VSAttr ='03' And $_I = 0
$IsSSD = $IsSSD + 2
EndIf
; attribute 0x08 (Seek Time Performance) with a value of 0
$_I = $_aVSpec[$_O + 3] ; Attribute Value
If $_VSAttr ='08' And $_I = 0
$IsSSD = $IsSSD + 4
EndIf
If $_ReturnVSA
If $_VSAttr <> '00'
$_OutString = $_OutString + $_VSAttr + ','
$_OutString = $_OutString + Right(' ' + $_aVSpec[$_O + 3], 3) + ',' ; Current
$_OutString = $_OutString + Right(' ' + $_aVSpec[$_O + 4], 3) + ',' ; Worst
$_OutString = $_OutString + Right(' ' + $_aVSpec[$_O + 1], 3) + ',' ; Threshold
$_OutString = $_OutString + Right('00' + DecToHex($_aVSpec[$_O + 10]), 2) ; RAW Data bytes
$_OutString = $_OutString + Right('00' + DecToHex($_aVSpec[$_O + 9]), 2)
$_OutString = $_OutString + Right('00' + DecToHex($_aVSpec[$_O + 8]), 2)
$_OutString = $_OutString + Right('00' + DecToHex($_aVSpec[$_O + 7]), 2)
$_OutString = $_OutString + Right('00' + DecToHex($_aVSpec[$_O + 6]), 2)
$_OutString = $_OutString + Right('00' + DecToHex($_aVSpec[$_O + 5]), 2) + @CRLF
EndIf
EndIf
Next
Else
$IsSSD = $IsSSD + 128 ; SMART Data unavailable
If $_ReturnVSA $_ECode = 1 EndIf ; return Error if ReturnVSA is true
EndIf
Next
If $_ReturnVSA
$IsSSD = Split($_OutString, @CRLF) ; Return VSA array instead of SSD status
EndIf
Exit $_ECode
EndFunction