Атрибуты SMART через запрос WMI на языке C#
Формулировка задачи:
Здравствуйте, передо мной возникла задача: написание программы, которая оценивает техническое состояние жёсткого диска. Я выяснил, что это можно сделать с помощью технологии S.M.A.R.T.
Здесь: Расшифровка параметров S.M.A.R.T, я прочитал о параметрах S.M.A.R.T.
При дальнейшем изучении вопроса стало понятно, что мне проще и удобнее всего получить доступ к параметрам S.M.A.R.T. с помощью технологии Windows Management Instrumentation (WMI), запросом на специальном языке WQL. В сети я нашёл статью, в которой описывается получение атрибутов S.M.A.R.T.:
http://www.i-programmer.info/projects/38/208.html?start=1
То есть параметры S.M.A.R.T. содержатся в структуре данных VendorSpecific, фактически являющейся массивом из байтов.
Вот код, приведённый там:
[Вот то, что вывела программа в моём случае:
Un___Un___ID___(?)___Un___Vale_Wrst Raw Unknw
10___0____1____15___0____100__253__0____0____0____0____0
0____0____3____3____0____99___99___0____0____0____0____0
0____0____4____50___0____98___98___64___10___0____0____0
0____0____5____51___0____100__100__0____0____0____0____0
0____0____7____15___0____87___60___229__122__192__28___1
0____0____9____50___0____94___94___151__21___0____0____0
0____0____10___19___0____100__100__0____0____0____0____0
0____0____12___50___0____98___98___66___10___0____0____0
0____0____187__50___0____100__100__0____0____0____0____0
Таким образом, программа вывела почти все значения атрибутов: ID, Value, Worst, и RAW, кроме Threshold. И ещё загадочный Status (?).
Вопрос: где взять Threshold, ведь он очень важен - позволяет оценить состояние атрибута.
Поле Status им точно не является, т.к. это значение не совпадает с тем, которое вывела другая программа SpeedFun.
Буду очень благодарен за помощь в поисках этого Threshold.
ManagementObjectSearcher WMISearch = new ManagementObjectSearcher();
WMISearch.Scope = new ManagementScope(@"\root\wmi");
WMISearch.Query = new ObjectQuery("Select * from MSStorageDriver_FailurePredictData");
ManagementObjectCollection FailDataSet = WMISearch.Get();
richTextBox1.Text = "Unknw\tUnknw\tAttribute\tStatus\tUnknw\tValue\tWorst\tRaw\t\tUnknw\n";
foreach ( ManagementObject FailData in FailDataSet )
{
Byte[] data = (Byte[])FailData.Properties["VendorSpecific"].Value;
for (int i = 0; i < data[0]-1; i++)
{
for (int j = 0; j < 12; j++)
{
richTextBox1.Text += data[i*12+j] + "\t";
}
richTextBox1.Text += "\n";
}
}Решение задачи: «Атрибуты SMART через запрос WMI на языке C#»
textual
Листинг программы
using System;
using System.Collections.Generic;
using System.Management;
public class HDD
{
public int Index { get; set; }
public bool IsOK { get; set; }
public string Model { get; set; }
public string Type { get; set; }
public string Serial { get; set; }
public Dictionary<int, Smart> Attributes = new Dictionary<int, Smart>() {
{0x00, new Smart("Invalid")},
{0x01, new Smart("Raw read error rate")},
{0x02, new Smart("Throughput performance")},
{0x03, new Smart("Spinup time")},
{0x04, new Smart("Start/Stop count")},
{0x05, new Smart("Reallocated sector count")},
{0x06, new Smart("Read channel margin")},
{0x07, new Smart("Seek error rate")},
{0x08, new Smart("Seek timer performance")},
{0x09, new Smart("Power-on hours count")},
{0x0A, new Smart("Spinup retry count")},
{0x0B, new Smart("Calibration retry count")},
{0x0C, new Smart("Power cycle count")},
{0x0D, new Smart("Soft read error rate")},
{0xB8, new Smart("End-to-End error")},
{0xBE, new Smart("Airflow Temperature")},
{0xBF, new Smart("G-sense error rate")},
{0xC0, new Smart("Power-off retract count")},
{0xC1, new Smart("Load/Unload cycle count")},
{0xC2, new Smart("HDD temperature")},
{0xC3, new Smart("Hardware ECC recovered")},
{0xC4, new Smart("Reallocation count")},
{0xC5, new Smart("Current pending sector count")},
{0xC6, new Smart("Offline scan uncorrectable count")},
{0xC7, new Smart("UDMA CRC error rate")},
{0xC8, new Smart("Write error rate")},
{0xC9, new Smart("Soft read error rate")},
{0xCA, new Smart("Data Address Mark errors")},
{0xCB, new Smart("Run out cancel")},
{0xCC, new Smart("Soft ECC correction")},
{0xCD, new Smart("Thermal asperity rate (TAR)")},
{0xCE, new Smart("Flying height")},
{0xCF, new Smart("Spin high current")},
{0xD0, new Smart("Spin buzz")},
{0xD1, new Smart("Offline seek performance")},
{0xDC, new Smart("Disk shift")},
{0xDD, new Smart("G-sense error rate")},
{0xDE, new Smart("Loaded hours")},
{0xDF, new Smart("Load/unload retry count")},
{0xE0, new Smart("Load friction")},
{0xE1, new Smart("Load/Unload cycle count")},
{0xE2, new Smart("Load-in time")},
{0xE3, new Smart("Torque amplification count")},
{0xE4, new Smart("Power-off retract count")},
{0xE6, new Smart("GMR head amplitude")},
{0xE7, new Smart("Temperature")},
{0xF0, new Smart("Head flying hours")},
{0xFA, new Smart("Read error retry rate")},
/* slot in any new codes you find in here */
};
}
public class Smart
{
public bool HasData
{
get
{
if (Current == 0 && Worst == 0 && Threshold == 0 && Data == 0)
return false;
return true;
}
}
public string Attribute { get; set; }
public int Current { get; set; }
public int Worst { get; set; }
public int Threshold { get; set; }
public int Data { get; set; }
public bool IsOK { get; set; }
public Smart()
{
}
public Smart(string attributeName)
{
this.Attribute = attributeName;
}
}
/// <summary>
/// Tested against Crystal Disk Info 5.3.1 and HD Tune Pro 3.5 on 15 Feb 2013.
/// Findings; I do not trust the individual smart register "OK" status reported back frm the drives.
/// I have tested faulty drives and they return an OK status on nearly all applications except HD Tune.
/// After further research I see HD Tune is checking specific attribute values against their thresholds
/// and and making a determination of their own (which is good) for whether the disk is in good condition or not.
/// I recommend whoever uses this code to do the same. For example -->
/// "Reallocated sector count" - the general threshold is 36, but even if 1 sector is reallocated I want to know about it and it should be flagged.
/// </summary>
public class Program
{
public static void Main()
{
try
{
// retrieve list of drives on computer (this will return both HDD's and CDROM's and Virtual CDROM's)
var dicDrives = new Dictionary<int, HDD>();
var wdSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
// extract model and interface information
int iDriveIndex = 0;
foreach (ManagementObject drive in wdSearcher.Get())
{
var hdd = new HDD();
hdd.Model = drive["Model"].ToString().Trim();
hdd.Type = drive["InterfaceType"].ToString().Trim();
dicDrives.Add(iDriveIndex, hdd);
iDriveIndex++;
}
var pmsearcher = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");
// retrieve hdd serial number
iDriveIndex = 0;
foreach (ManagementObject drive in pmsearcher.Get())
{
// because all physical media will be returned we need to exit
// after the hard drives serial info is extracted
if (iDriveIndex >= dicDrives.Count)
break;
dicDrives[iDriveIndex].Serial = drive["SerialNumber"] == null ? "None" : drive["SerialNumber"].ToString().Trim();
iDriveIndex++;
}
// get wmi access to hdd
var searcher = new ManagementObjectSearcher("Select * from Win32_DiskDrive");
searcher.Scope = new ManagementScope(@"\root\wmi");
// check if SMART reports the drive is failing
searcher.Query = new ObjectQuery("Select * from MSStorageDriver_FailurePredictStatus");
iDriveIndex = 0;
foreach (ManagementObject drive in searcher.Get())
{
dicDrives[iDriveIndex].IsOK = (bool)drive.Properties["PredictFailure"].Value == false;
iDriveIndex++;
}
// retrive attribute flags, value worste and vendor data information
searcher.Query = new ObjectQuery("Select * from MSStorageDriver_FailurePredictData");
iDriveIndex = 0;
foreach (ManagementObject data in searcher.Get())
{
Byte[] bytes = (Byte[])data.Properties["VendorSpecific"].Value;
for (int i = 0; i < 30; ++i)
{
try
{
int id = bytes[i * 12 + 2];
int flags = bytes[i * 12 + 4]; // least significant status byte, +3 most significant byte, but not used so ignored.
//bool advisory = (flags & 0x1) == 0x0;
bool failureImminent = (flags & 0x1) == 0x1;
//bool onlineDataCollection = (flags & 0x2) == 0x2;
int value = bytes[i * 12 + 5];
int worst = bytes[i * 12 + 6];
int vendordata = BitConverter.ToInt32(bytes, i * 12 + 7);
if (id == 0) continue;
var attr = dicDrives[iDriveIndex].Attributes[id];
attr.Current = value;
attr.Worst = worst;
attr.Data = vendordata;
attr.IsOK = failureImminent == false;
}
catch
{
// given key does not exist in attribute collection (attribute not in the dictionary of attributes)
}
}
iDriveIndex++;
}
// retreive threshold values foreach attribute
searcher.Query = new ObjectQuery("Select * from MSStorageDriver_FailurePredictThresholds");
iDriveIndex = 0;
foreach (ManagementObject data in searcher.Get())
{
Byte[] bytes = (Byte[])data.Properties["VendorSpecific"].Value;
for (int i = 0; i < 30; ++i)
{
try
{
int id = bytes[i * 12 + 2];
int thresh = bytes[i * 12 + 3];
if (id == 0) continue;
var attr = dicDrives[iDriveIndex].Attributes[id];
attr.Threshold = thresh;
}
catch
{
// given key does not exist in attribute collection (attribute not in the dictionary of attributes)
}
}
iDriveIndex++;
}
// print
foreach (var drive in dicDrives)
{
Console.WriteLine("-----------------------------------------------------");
Console.WriteLine(" DRIVE ({0}): " + drive.Value.Serial + " - " + drive.Value.Model + " - " + drive.Value.Type, ((drive.Value.IsOK) ? "OK" : "BAD"));
Console.WriteLine("-----------------------------------------------------");
Console.WriteLine("");
Console.WriteLine("ID Current Worst Threshold Data Status");
foreach (var attr in drive.Value.Attributes)
{
if (attr.Value.HasData)
Console.WriteLine("{0}\t {1}\t {2}\t {3}\t " + attr.Value.Data + " " + ((attr.Value.IsOK) ? "OK" : ""), attr.Value.Attribute, attr.Value.Current, attr.Value.Worst, attr.Value.Threshold);
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
}
Console.ReadLine();
}
catch (ManagementException e)
{
Console.WriteLine("An error occurred while querying for WMI data: " + e.Message);
}
}
}