Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load/Unload Cycle Count using S.M.A.R.T and WMI

In an attempt to develop a hard disk analytic tool, I'm trying to get the value of Load/Unload cycle count from my hard disk's S.M.A.R.T data, I'm wondering if anyone knows how to do this. What I'm trying:

  1. I'm searching the WMI MSStorageDriver_ATAPISmartData class data where attribute number 193 is what i need (the attribute representing load/unload cycle count)
  2. The data I'm getting looks like

enter image description here

I think I'm close, the data in red is the same as what Everest Home edition is showing when i run it, ideally i would want the last part which is (attribute called data)

enter image description here

Method for collecting this data:

static void doStuff()
{
    try
    {

        byte TEMPERATURE_ATTRIBUTE = 193;

        ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"\root\WMI", "SELECT * FROM MSStorageDriver_ATAPISmartData");
        //loop through all the hard disks
        foreach (ManagementObject queryObj in searcher.Get())
        {
            byte[] arrVendorSpecific = (byte[])queryObj.GetPropertyValue("VendorSpecific");

            int tempIndex = Array.IndexOf(arrVendorSpecific, TEMPERATURE_ATTRIBUTE);
            Console.WriteLine("HDD TEMP: " + arrVendorSpecific[tempIndex + 5].ToString());

            foreach (byte dat in arrVendorSpecific)
            {
                Console.Write(dat.ToString() + " ");
            }
        }

    }
    catch (Exception err) { Console.WriteLine(err.Message); }
}

P.S. this method works for collecting the HDD's temperature (that's what the Console.WriteLine("HDD TEMP: " + arrVendorSpecific[tempIndex + 5].ToString()); line is all about but I'm not sure why its tempIndex+5

like image 753
Lyuben Todorov Avatar asked Jan 18 '23 02:01

Lyuben Todorov


1 Answers

The code which you are using is not correct, because you are using a secuencial search (Array.IndexOf) to find the S.M.A.R.T Attribute ID (you can have false positives because that value can match with another in the array), the ID of these attibutes has an fixed position inside of an documented structure (SMART Attribute Overview).

SMART Attribute Table

Offset  Length  Description
        (bytes) 
0         2      SMART structure version (this is vendor-specific)
2         12     Attribute entry 1
2+(12)    12     Attribute entry 2
. . .
2+(12*29) 12     Attribute entry 30

Entry in the Attribute Table

enter image description here

from here you can write a code to search the location of each attribute and get the values which you are looking for

using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
using System.Runtime.InteropServices;

namespace GetWMI_Info
{
    class Program
    {

        [StructLayout(LayoutKind.Sequential)]
        public struct Attribute
        {
            public byte AttributeID;
            public ushort Flags;
            public byte Value;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] VendorData;
        }

        static void Main(string[] args)
        {
            try
            {
                Attribute AtributeInfo;
                ManagementScope Scope = new ManagementScope(String.Format("\\\\{0}\\root\\WMI", "localhost"), null);
                Scope.Connect();
                ObjectQuery Query = new ObjectQuery("SELECT VendorSpecific FROM MSStorageDriver_ATAPISmartData");
                ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query);
                byte LoadCycleCount = 0xC1;
                int Delta  = 12;
                foreach (ManagementObject WmiObject in Searcher.Get())
                {
                    byte[] VendorSpecific = (byte[])WmiObject["VendorSpecific"];
                    for (int offset = 2; offset < VendorSpecific.Length; )
                    {
                        if (VendorSpecific[offset] == LoadCycleCount)
                        {

                            IntPtr buffer = IntPtr.Zero;
                            try
                            {
                                buffer = Marshal.AllocHGlobal(Delta);
                                Marshal.Copy(VendorSpecific, offset, buffer, Delta);
                                AtributeInfo = (Attribute)Marshal.PtrToStructure(buffer, typeof(Attribute));
                                Console.WriteLine("AttributeID {0}", AtributeInfo.AttributeID);
                                Console.WriteLine("Flags {0}", AtributeInfo.Flags);
                                Console.WriteLine("Value {0}", AtributeInfo.Value);
                                Console.WriteLine("Value {0}", BitConverter.ToString(AtributeInfo.VendorData));
                            }
                            finally
                            {
                                if (buffer != IntPtr.Zero)
                                {
                                    Marshal.FreeHGlobal(buffer);
                                }
                            }                                                
                        }
                        offset += Delta;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(String.Format("Exception {0} Trace {1}",e.Message,e.StackTrace));
            }
            Console.WriteLine("Press Enter to exit");
            Console.Read();
        }
    }
}
like image 142
RRUZ Avatar answered Jan 22 '23 19:01

RRUZ