如何在 C# .NET 中读取 HDD S.M.A.R.T 寄存器并获取该寄存器的所有相关值?

Posted

技术标签:

【中文标题】如何在 C# .NET 中读取 HDD S.M.A.R.T 寄存器并获取该寄存器的所有相关值?【英文标题】:How can I read HDD S.M.A.R.T registers in C# .NET and obtain all the associated values for that register? 【发布时间】:2013-01-19 06:16:52 【问题描述】:

如何使用 WMI 或其他方式在 .NET 中读取 HDD 的 SMART 寄存器,从而获得以下数据:

注册名称 当前值 最差价值 阈值 数据(供应商) 智能状态

【问题讨论】:

也许***.com/questions/9352017/… 不走运,我已经测试了我在 ***.com 上找到的所有“C# HDD SMART”解决方案。 【参考方案1】:

经过一些工作,我整理了代码,使用 C#/WMI 返回了有关 HDD 的所有相关 SMART 信息的综合报告。

/*
Copyright (c) 2013, Llewellyn Kruger
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/
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);
      
    
  

更新 - 2017 年 12 月 19 日

我已经建立了一个类库,可以简化查询您需要的所有智能数据。它包括一个演示项目。

GitHub 仓库: Smart.Net Library

用法:

var drives = Simplified.IO.Smart.GetDrives(); // Static Method

【讨论】:

请注意,这里不鼓励裸链接到您自己的网站/产品,原因有两个;首先,答案应该作为独立的答案发布,而不仅仅是指向外部站点的链接。其次,自我推销在这里往往不受欢迎,并且经常被标记为垃圾邮件(特别是如果没有披露您链接到自己的网站/产品)。 嗨,安德鲁,我现在才来得及回复。查看***,我看到很多只是链接的答案。在这种情况下,我也做了同样的事情,但没有看到问题。其次,关于您对自我推销的评论;我不关心其他人皱眉的事情。事实是,没有人为我的问题提供解决方案。我花了我的时间和金钱将解决方案放在一起并与全世界分享。像您这样的评论会影响 *** 追随者跟随套件并否决一个好的答案。没有人会赢。 不再链接到博客,相关链接中没有关于此代码的信息。这就是为什么堆栈溢出更喜欢没有一行链接答案的原因。

以上是关于如何在 C# .NET 中读取 HDD S.M.A.R.T 寄存器并获取该寄存器的所有相关值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# .NET 中保持 TCP 连接打开并执行多次写入/读取?

如何在 C# 和 ASP.NET MVC 中读取/写入 cookie

如何从 C# 读取 VB.NET 项目中的“app.config”文件

将 HDD Serial # VB6 代码转换为 VB.NET 代码

Asp.net读取Word

RAMdisk 读取似乎并不比硬盘 (HDD) 读取快