;; 
;;=====================================================================================----- 
;; 
;;FUNCTION       IsSSD() 
;; 
;;ACTION         Determines if a disk is SSD or Rotating Media 
;; 
;;AUTHOR         Glenn Barnas 
;; 
;;VERSION        1.0 - 2014/12/05 
;; 
;;HISTORY        1.0 - 2014/12/05 - Initial Release 
;; 
;;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        Integer - binary value 
;;               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