Hardware/HDD/WindowsSmart.cs
author moel.mich
Tue, 30 Dec 2014 21:04:54 +0000
changeset 430 6b24e39f1b84
parent 344 3145aadca3d2
permissions -rw-r--r--
Fixed Issue 651.
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	Copyright (C) 2010 Paul Werelds
     9   Copyright (C) 2011 Roland Reinl <roland-reinl@gmx.de>
    10 	
    11 */
    12 
    13 using System;
    14 using System.Collections.Generic;
    15 using System.Management;
    16 using System.Runtime.InteropServices;
    17 
    18 namespace OpenHardwareMonitor.Hardware.HDD {
    19 
    20   internal class WindowsSmart : ISmart {
    21     [Flags]
    22     protected enum AccessMode : uint {     
    23       Read = 0x80000000,    
    24       Write = 0x40000000,     
    25       Execute = 0x20000000,     
    26       All = 0x10000000
    27     }
    28 
    29     [Flags]
    30     protected enum ShareMode : uint {
    31       None = 0,     
    32       Read = 1,     
    33       Write = 2,    
    34       Delete = 4
    35     }
    36 
    37     protected enum CreationMode : uint {
    38       New = 1,
    39       CreateAlways = 2,    
    40       OpenExisting = 3,    
    41       OpenAlways = 4,    
    42       TruncateExisting = 5
    43     }
    44 
    45     [Flags]
    46     protected enum FileAttribute : uint {
    47       Readonly = 0x00000001,
    48       Hidden = 0x00000002,
    49       System = 0x00000004,
    50       Directory = 0x00000010,
    51       Archive = 0x00000020,
    52       Device = 0x00000040,
    53       Normal = 0x00000080,
    54       Temporary = 0x00000100,
    55       SparseFile = 0x00000200,
    56       ReparsePoint = 0x00000400,
    57       Compressed = 0x00000800,
    58       Offline = 0x00001000,
    59       NotContentIndexed = 0x00002000,
    60       Encrypted = 0x00004000,
    61     }
    62 
    63     protected enum DriveCommand : uint {
    64       GetVersion = 0x00074080,
    65       SendDriveCommand = 0x0007c084,
    66       ReceiveDriveData = 0x0007c088
    67     }
    68 
    69     protected enum RegisterCommand : byte {
    70       /// <summary>
    71       /// SMART data requested.
    72       /// </summary>
    73       SmartCmd = 0xB0,
    74 
    75       /// <summary>
    76       /// Identify data is requested.
    77       /// </summary>
    78       IdCmd = 0xEC,
    79     }
    80 
    81     protected enum RegisterFeature : byte {
    82       /// <summary>
    83       /// Read SMART data.
    84       /// </summary>
    85       SmartReadData = 0xD0,
    86 
    87       /// <summary>
    88       /// Read SMART thresholds.
    89       /// </summary>
    90       SmartReadThresholds = 0xD1, /* obsolete */
    91 
    92       /// <summary>
    93       /// Autosave SMART data.
    94       /// </summary>
    95       SmartAutosave = 0xD2,
    96 
    97       /// <summary>
    98       /// Save SMART attributes.
    99       /// </summary>
   100       SmartSaveAttr = 0xD3,
   101 
   102       /// <summary>
   103       /// Set SMART to offline immediately.
   104       /// </summary>
   105       SmartImmediateOffline = 0xD4,
   106 
   107       /// <summary>
   108       /// Read SMART log.
   109       /// </summary>
   110       SmartReadLog = 0xD5,
   111 
   112       /// <summary>
   113       /// Write SMART log.
   114       /// </summary>
   115       SmartWriteLog = 0xD6,
   116 
   117       /// <summary>
   118       /// Write SMART thresholds.
   119       /// </summary>
   120       SmartWriteThresholds = 0xD7, /* obsolete */
   121 
   122       /// <summary>
   123       /// Enable SMART.
   124       /// </summary>
   125       SmartEnableOperations = 0xD8,
   126 
   127       /// <summary>
   128       /// Disable SMART.
   129       /// </summary>
   130       SmartDisableOperations = 0xD9,
   131 
   132       /// <summary>
   133       /// Get SMART status.
   134       /// </summary>
   135       SmartStatus = 0xDA,
   136 
   137       /// <summary>
   138       /// Set SMART to offline automatically.
   139       /// </summary>
   140       SmartAutoOffline = 0xDB, /* obsolete */
   141     }
   142 
   143     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   144     protected struct CommandBlockRegisters {
   145       public RegisterFeature Features;         
   146       public byte SectorCount;      
   147       public byte LBALow;       
   148       public byte LBAMid;           
   149       public byte LBAHigh;        
   150       public byte Device;
   151       public RegisterCommand Command;           
   152       public byte Reserved;                  
   153     }
   154 
   155     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   156     protected struct DriveCommandParameter {
   157       public uint BufferSize;           
   158       public CommandBlockRegisters Registers;           
   159       public byte DriveNumber;   
   160       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   161       public byte[] Reserved;                                
   162     }
   163 
   164     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   165     protected struct DriverStatus {
   166       public byte DriverError;   
   167       public byte IDEError;             
   168       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
   169       public byte[] Reserved;               
   170     }
   171 
   172     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   173     protected struct DriveCommandResult {
   174       public uint BufferSize;
   175       public DriverStatus DriverStatus;
   176     } 
   177 
   178     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   179     protected struct DriveSmartReadDataResult {
   180       public uint BufferSize;           
   181       public DriverStatus DriverStatus;
   182       public byte Version;
   183       public byte Reserved;
   184       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   185       public DriveAttributeValue[] Attributes;                                                                                       
   186     }
   187 
   188     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   189     protected struct DriveSmartReadThresholdsResult {
   190       public uint BufferSize;
   191       public DriverStatus DriverStatus;
   192       public byte Version;
   193       public byte Reserved;
   194       [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
   195       public DriveThresholdValue[] Thresholds;
   196     }
   197 
   198     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   199     protected struct Identify {
   200       public ushort GeneralConfiguration;
   201       public ushort NumberOfCylinders;
   202       public ushort Reserved;
   203       public ushort NumberOfHeads;
   204       public ushort UnformattedBytesPerTrack;
   205       public ushort UnformattedBytesPerSector;
   206       public ushort SectorsPerTrack;
   207       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
   208       public ushort[] VendorUnique;
   209       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
   210       public byte[] SerialNumber;
   211       public ushort BufferType;
   212       public ushort BufferSectorSize;
   213       public ushort NumberOfEccBytes;
   214       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
   215       public byte[] FirmwareRevision;
   216       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
   217       public byte[] ModelNumber;
   218       public ushort MoreVendorUnique;
   219       public ushort DoubleWordIo;
   220       public ushort Capabilities;
   221       public ushort MoreReserved;
   222       public ushort PioCycleTimingMode;
   223       public ushort DmaCycleTimingMode;
   224       [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
   225       public byte[] More;
   226     }
   227 
   228     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   229     protected struct DriveIdentifyResult {
   230       public uint BufferSize;
   231       public DriverStatus DriverStatus;
   232       public Identify Identify;
   233     }
   234 
   235     public IntPtr InvalidHandle { get { return (IntPtr)(-1); } }
   236 
   237     private const byte SMART_LBA_MID = 0x4F;
   238     private const byte SMART_LBA_HI = 0xC2;
   239 
   240     private const int MAX_DRIVE_ATTRIBUTES = 512;
   241 
   242     public IntPtr OpenDrive(int driveNumber) {
   243       return NativeMethods.CreateFile(@"\\.\PhysicalDrive" + driveNumber,
   244         AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
   245         IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
   246         IntPtr.Zero);
   247     }
   248 
   249     public bool EnableSmart(IntPtr handle, int driveNumber) {
   250       DriveCommandParameter parameter = new DriveCommandParameter();
   251       DriveCommandResult result;
   252       uint bytesReturned;
   253 
   254       parameter.DriveNumber = (byte)driveNumber;
   255       parameter.Registers.Features = RegisterFeature.SmartEnableOperations;
   256       parameter.Registers.LBAMid = SMART_LBA_MID;
   257       parameter.Registers.LBAHigh = SMART_LBA_HI;
   258       parameter.Registers.Command = RegisterCommand.SmartCmd;
   259 
   260       return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, 
   261         ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
   262         Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, 
   263         IntPtr.Zero);
   264     }
   265 
   266     public DriveAttributeValue[] ReadSmartData(IntPtr handle, int driveNumber) {
   267       DriveCommandParameter parameter = new DriveCommandParameter();
   268       DriveSmartReadDataResult result;
   269       uint bytesReturned;
   270 
   271       parameter.DriveNumber = (byte)driveNumber;
   272       parameter.Registers.Features = RegisterFeature.SmartReadData;
   273       parameter.Registers.LBAMid = SMART_LBA_MID;
   274       parameter.Registers.LBAHigh = SMART_LBA_HI;
   275       parameter.Registers.Command = RegisterCommand.SmartCmd;
   276 
   277       bool isValid = NativeMethods.DeviceIoControl(handle, 
   278         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   279         out result, Marshal.SizeOf(typeof(DriveSmartReadDataResult)), 
   280         out bytesReturned, IntPtr.Zero);
   281 
   282       return (isValid) ? result.Attributes : new DriveAttributeValue[0];
   283     }
   284 
   285     public DriveThresholdValue[] ReadSmartThresholds(IntPtr handle,
   286       int driveNumber) 
   287     {
   288       DriveCommandParameter parameter = new DriveCommandParameter();
   289       DriveSmartReadThresholdsResult result;
   290       uint bytesReturned = 0;
   291 
   292       parameter.DriveNumber = (byte)driveNumber;
   293       parameter.Registers.Features = RegisterFeature.SmartReadThresholds;
   294       parameter.Registers.LBAMid = SMART_LBA_MID;
   295       parameter.Registers.LBAHigh = SMART_LBA_HI;
   296       parameter.Registers.Command = RegisterCommand.SmartCmd;
   297 
   298       bool isValid = NativeMethods.DeviceIoControl(handle,
   299         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter),
   300         out result, Marshal.SizeOf(typeof(DriveSmartReadThresholdsResult)), 
   301         out bytesReturned, IntPtr.Zero); 
   302 
   303       return (isValid) ? result.Thresholds : new DriveThresholdValue[0];
   304     }
   305 
   306     private string GetString(byte[] bytes) {   
   307       char[] chars = new char[bytes.Length];
   308       for (int i = 0; i < bytes.Length; i += 2) {
   309         chars[i] = (char)bytes[i + 1];
   310         chars[i + 1] = (char)bytes[i];
   311       }
   312       return new string(chars).Trim(new char[] { ' ', '\0' });
   313     }
   314 
   315     public bool ReadNameAndFirmwareRevision(IntPtr handle, int driveNumber, 
   316       out string name, out string firmwareRevision) 
   317     {
   318       DriveCommandParameter parameter = new DriveCommandParameter();
   319       DriveIdentifyResult result;
   320       uint bytesReturned;
   321 
   322       parameter.DriveNumber = (byte)driveNumber;
   323       parameter.Registers.Command = RegisterCommand.IdCmd;
   324 
   325       bool valid = NativeMethods.DeviceIoControl(handle, 
   326         DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), 
   327         out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), 
   328         out bytesReturned, IntPtr.Zero);
   329 
   330       if (!valid) {
   331         name = null;
   332         firmwareRevision = null;
   333         return false;
   334       }
   335 
   336       name = GetString(result.Identify.ModelNumber);
   337       firmwareRevision = GetString(result.Identify.FirmwareRevision);
   338       return true;
   339     }
   340 
   341     public void CloseHandle(IntPtr handle) {
   342       NativeMethods.CloseHandle(handle);
   343     }
   344 
   345     public string[] GetLogicalDrives(int driveIndex) {
   346       List<string> list = new List<string>();
   347       try {
   348         using (ManagementObjectSearcher s = new ManagementObjectSearcher(
   349             "root\\CIMV2",
   350             "SELECT * FROM Win32_DiskPartition " +
   351             "WHERE DiskIndex = " + driveIndex))
   352         using (ManagementObjectCollection dpc = s.Get())
   353         foreach (ManagementObject dp in dpc) 
   354           using (ManagementObjectCollection ldc = 
   355             dp.GetRelated("Win32_LogicalDisk"))
   356           foreach (ManagementBaseObject ld in ldc) 
   357             list.Add(((string)ld["Name"]).TrimEnd(':')); 
   358       } catch { }
   359       return list.ToArray();
   360     }
   361 
   362     protected static class NativeMethods {
   363       private const string KERNEL = "kernel32.dll";
   364 
   365       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
   366         CharSet = CharSet.Unicode)]
   367       public static extern IntPtr CreateFile(string fileName,
   368         AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
   369         CreationMode creationDisposition, FileAttribute flagsAndAttributes,
   370         IntPtr templateFilehandle);
   371 
   372       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   373       public static extern int CloseHandle(IntPtr handle);
   374 
   375       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   376       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   377       public static extern bool DeviceIoControl(IntPtr handle,
   378         DriveCommand command, ref DriveCommandParameter parameter,
   379         int parameterSize, out DriveSmartReadDataResult result, int resultSize,
   380         out uint bytesReturned, IntPtr overlapped);
   381 
   382       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   383       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   384       public static extern bool DeviceIoControl(IntPtr handle,
   385         DriveCommand command, ref DriveCommandParameter parameter,
   386         int parameterSize, out DriveSmartReadThresholdsResult result, 
   387         int resultSize, out uint bytesReturned, IntPtr overlapped);
   388 
   389       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   390       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   391       public static extern bool DeviceIoControl(IntPtr handle,
   392         DriveCommand command, ref DriveCommandParameter parameter,
   393         int parameterSize, out DriveCommandResult result, int resultSize,
   394         out uint bytesReturned, IntPtr overlapped);
   395 
   396       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   397       [return: MarshalAsAttribute(UnmanagedType.Bool)]
   398       public static extern bool DeviceIoControl(IntPtr handle,
   399         DriveCommand command, ref DriveCommandParameter parameter,
   400         int parameterSize, out DriveIdentifyResult result, int resultSize,
   401         out uint bytesReturned, IntPtr overlapped);
   402     }    
   403   }
   404 }