moel@324: /* moel@324: moel@344: This Source Code Form is subject to the terms of the Mozilla Public moel@344: License, v. 2.0. If a copy of the MPL was not distributed with this moel@344: file, You can obtain one at http://mozilla.org/MPL/2.0/. moel@324: moel@344: Copyright (C) 2009-2012 Michael Möller moel@344: Copyright (C) 2010 Paul Werelds moel@344: Copyright (C) 2011 Roland Reinl moel@344: moel@324: */ moel@324: moel@324: using System; moel@324: using System.Collections.Generic; moel@369: using System.Management; moel@324: using System.Runtime.InteropServices; moel@324: moel@324: namespace OpenHardwareMonitor.Hardware.HDD { moel@324: moel@324: internal class WindowsSmart : ISmart { moel@324: [Flags] moel@324: protected enum AccessMode : uint { moel@324: Read = 0x80000000, moel@324: Write = 0x40000000, moel@324: Execute = 0x20000000, moel@324: All = 0x10000000 moel@324: } moel@324: moel@324: [Flags] moel@324: protected enum ShareMode : uint { moel@324: None = 0, moel@324: Read = 1, moel@324: Write = 2, moel@324: Delete = 4 moel@324: } moel@324: moel@324: protected enum CreationMode : uint { moel@324: New = 1, moel@324: CreateAlways = 2, moel@324: OpenExisting = 3, moel@324: OpenAlways = 4, moel@324: TruncateExisting = 5 moel@324: } moel@324: moel@324: [Flags] moel@324: protected enum FileAttribute : uint { moel@324: Readonly = 0x00000001, moel@324: Hidden = 0x00000002, moel@324: System = 0x00000004, moel@324: Directory = 0x00000010, moel@324: Archive = 0x00000020, moel@324: Device = 0x00000040, moel@324: Normal = 0x00000080, moel@324: Temporary = 0x00000100, moel@324: SparseFile = 0x00000200, moel@324: ReparsePoint = 0x00000400, moel@324: Compressed = 0x00000800, moel@324: Offline = 0x00001000, moel@324: NotContentIndexed = 0x00002000, moel@324: Encrypted = 0x00004000, moel@324: } moel@324: moel@324: protected enum DriveCommand : uint { moel@324: GetVersion = 0x00074080, moel@324: SendDriveCommand = 0x0007c084, moel@324: ReceiveDriveData = 0x0007c088 moel@324: } moel@324: moel@324: protected enum RegisterCommand : byte { moel@324: /// moel@324: /// SMART data requested. moel@324: /// moel@324: SmartCmd = 0xB0, moel@324: moel@324: /// moel@324: /// Identify data is requested. moel@324: /// moel@324: IdCmd = 0xEC, moel@324: } moel@324: moel@324: protected enum RegisterFeature : byte { moel@324: /// moel@324: /// Read SMART data. moel@324: /// moel@324: SmartReadData = 0xD0, moel@324: moel@324: /// moel@324: /// Read SMART thresholds. moel@324: /// moel@324: SmartReadThresholds = 0xD1, /* obsolete */ moel@324: moel@324: /// moel@324: /// Autosave SMART data. moel@324: /// moel@324: SmartAutosave = 0xD2, moel@324: moel@324: /// moel@324: /// Save SMART attributes. moel@324: /// moel@324: SmartSaveAttr = 0xD3, moel@324: moel@324: /// moel@324: /// Set SMART to offline immediately. moel@324: /// moel@324: SmartImmediateOffline = 0xD4, moel@324: moel@324: /// moel@324: /// Read SMART log. moel@324: /// moel@324: SmartReadLog = 0xD5, moel@324: moel@324: /// moel@324: /// Write SMART log. moel@324: /// moel@324: SmartWriteLog = 0xD6, moel@324: moel@324: /// moel@324: /// Write SMART thresholds. moel@324: /// moel@324: SmartWriteThresholds = 0xD7, /* obsolete */ moel@324: moel@324: /// moel@324: /// Enable SMART. moel@324: /// moel@324: SmartEnableOperations = 0xD8, moel@324: moel@324: /// moel@324: /// Disable SMART. moel@324: /// moel@324: SmartDisableOperations = 0xD9, moel@324: moel@324: /// moel@324: /// Get SMART status. moel@324: /// moel@324: SmartStatus = 0xDA, moel@324: moel@324: /// moel@324: /// Set SMART to offline automatically. moel@324: /// moel@324: SmartAutoOffline = 0xDB, /* obsolete */ moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct CommandBlockRegisters { moel@324: public RegisterFeature Features; moel@324: public byte SectorCount; moel@324: public byte LBALow; moel@324: public byte LBAMid; moel@324: public byte LBAHigh; moel@324: public byte Device; moel@324: public RegisterCommand Command; moel@324: public byte Reserved; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct DriveCommandParameter { moel@324: public uint BufferSize; moel@324: public CommandBlockRegisters Registers; moel@324: public byte DriveNumber; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] moel@324: public byte[] Reserved; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct DriverStatus { moel@324: public byte DriverError; moel@324: public byte IDEError; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] moel@324: public byte[] Reserved; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct DriveCommandResult { moel@324: public uint BufferSize; moel@324: public DriverStatus DriverStatus; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct DriveSmartReadDataResult { moel@324: public uint BufferSize; moel@324: public DriverStatus DriverStatus; moel@324: public byte Version; moel@324: public byte Reserved; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)] moel@324: public DriveAttributeValue[] Attributes; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct DriveSmartReadThresholdsResult { moel@324: public uint BufferSize; moel@324: public DriverStatus DriverStatus; moel@324: public byte Version; moel@324: public byte Reserved; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)] moel@324: public DriveThresholdValue[] Thresholds; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct Identify { moel@324: public ushort GeneralConfiguration; moel@324: public ushort NumberOfCylinders; moel@324: public ushort Reserved; moel@324: public ushort NumberOfHeads; moel@324: public ushort UnformattedBytesPerTrack; moel@324: public ushort UnformattedBytesPerSector; moel@324: public ushort SectorsPerTrack; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] moel@324: public ushort[] VendorUnique; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] moel@324: public byte[] SerialNumber; moel@324: public ushort BufferType; moel@324: public ushort BufferSectorSize; moel@324: public ushort NumberOfEccBytes; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] moel@324: public byte[] FirmwareRevision; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)] moel@324: public byte[] ModelNumber; moel@324: public ushort MoreVendorUnique; moel@324: public ushort DoubleWordIo; moel@324: public ushort Capabilities; moel@324: public ushort MoreReserved; moel@324: public ushort PioCycleTimingMode; moel@324: public ushort DmaCycleTimingMode; moel@324: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)] moel@324: public byte[] More; moel@324: } moel@324: moel@324: [StructLayout(LayoutKind.Sequential, Pack = 1)] moel@324: protected struct DriveIdentifyResult { moel@324: public uint BufferSize; moel@324: public DriverStatus DriverStatus; moel@324: public Identify Identify; moel@324: } moel@324: moel@324: public IntPtr InvalidHandle { get { return (IntPtr)(-1); } } moel@324: moel@324: private const byte SMART_LBA_MID = 0x4F; moel@324: private const byte SMART_LBA_HI = 0xC2; moel@324: moel@324: private const int MAX_DRIVE_ATTRIBUTES = 512; moel@324: moel@324: public IntPtr OpenDrive(int driveNumber) { moel@324: return NativeMethods.CreateFile(@"\\.\PhysicalDrive" + driveNumber, moel@324: AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write, moel@324: IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device, moel@324: IntPtr.Zero); moel@324: } moel@324: moel@324: public bool EnableSmart(IntPtr handle, int driveNumber) { moel@324: DriveCommandParameter parameter = new DriveCommandParameter(); moel@324: DriveCommandResult result; moel@324: uint bytesReturned; moel@324: moel@324: parameter.DriveNumber = (byte)driveNumber; moel@324: parameter.Registers.Features = RegisterFeature.SmartEnableOperations; moel@324: parameter.Registers.LBAMid = SMART_LBA_MID; moel@324: parameter.Registers.LBAHigh = SMART_LBA_HI; moel@324: parameter.Registers.Command = RegisterCommand.SmartCmd; moel@324: moel@324: return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand, moel@324: ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result, moel@324: Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned, moel@324: IntPtr.Zero); moel@324: } moel@324: moel@324: public DriveAttributeValue[] ReadSmartData(IntPtr handle, int driveNumber) { moel@324: DriveCommandParameter parameter = new DriveCommandParameter(); moel@324: DriveSmartReadDataResult result; moel@324: uint bytesReturned; moel@324: moel@324: parameter.DriveNumber = (byte)driveNumber; moel@324: parameter.Registers.Features = RegisterFeature.SmartReadData; moel@324: parameter.Registers.LBAMid = SMART_LBA_MID; moel@324: parameter.Registers.LBAHigh = SMART_LBA_HI; moel@324: parameter.Registers.Command = RegisterCommand.SmartCmd; moel@324: moel@324: bool isValid = NativeMethods.DeviceIoControl(handle, moel@324: DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), moel@324: out result, Marshal.SizeOf(typeof(DriveSmartReadDataResult)), moel@324: out bytesReturned, IntPtr.Zero); moel@324: moel@324: return (isValid) ? result.Attributes : new DriveAttributeValue[0]; moel@324: } moel@324: moel@324: public DriveThresholdValue[] ReadSmartThresholds(IntPtr handle, moel@324: int driveNumber) moel@324: { moel@324: DriveCommandParameter parameter = new DriveCommandParameter(); moel@324: DriveSmartReadThresholdsResult result; moel@324: uint bytesReturned = 0; moel@324: moel@324: parameter.DriveNumber = (byte)driveNumber; moel@324: parameter.Registers.Features = RegisterFeature.SmartReadThresholds; moel@324: parameter.Registers.LBAMid = SMART_LBA_MID; moel@324: parameter.Registers.LBAHigh = SMART_LBA_HI; moel@324: parameter.Registers.Command = RegisterCommand.SmartCmd; moel@324: moel@324: bool isValid = NativeMethods.DeviceIoControl(handle, moel@324: DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), moel@324: out result, Marshal.SizeOf(typeof(DriveSmartReadThresholdsResult)), moel@324: out bytesReturned, IntPtr.Zero); moel@324: moel@324: return (isValid) ? result.Thresholds : new DriveThresholdValue[0]; moel@325: } moel@324: moel@325: private string GetString(byte[] bytes) { moel@325: char[] chars = new char[bytes.Length]; moel@325: for (int i = 0; i < bytes.Length; i += 2) { moel@325: chars[i] = (char)bytes[i + 1]; moel@325: chars[i + 1] = (char)bytes[i]; moel@325: } moel@325: return new string(chars).Trim(new char[] { ' ', '\0' }); moel@325: } moel@325: moel@325: public bool ReadNameAndFirmwareRevision(IntPtr handle, int driveNumber, moel@325: out string name, out string firmwareRevision) moel@325: { moel@324: DriveCommandParameter parameter = new DriveCommandParameter(); moel@324: DriveIdentifyResult result; moel@324: uint bytesReturned; moel@324: moel@324: parameter.DriveNumber = (byte)driveNumber; moel@324: parameter.Registers.Command = RegisterCommand.IdCmd; moel@324: moel@324: bool valid = NativeMethods.DeviceIoControl(handle, moel@324: DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter), moel@324: out result, Marshal.SizeOf(typeof(DriveIdentifyResult)), moel@324: out bytesReturned, IntPtr.Zero); moel@324: moel@325: if (!valid) { moel@325: name = null; moel@325: firmwareRevision = null; moel@325: return false; moel@325: } moel@324: moel@325: name = GetString(result.Identify.ModelNumber); moel@325: firmwareRevision = GetString(result.Identify.FirmwareRevision); moel@325: return true; moel@324: } moel@324: moel@324: public void CloseHandle(IntPtr handle) { moel@324: NativeMethods.CloseHandle(handle); moel@324: } moel@324: moel@369: public string[] GetLogicalDrives(int driveIndex) { moel@369: List list = new List(); moel@369: try { moel@369: using (ManagementObjectSearcher s = new ManagementObjectSearcher( moel@369: "root\\CIMV2", moel@369: "SELECT * FROM Win32_DiskPartition " + moel@369: "WHERE DiskIndex = " + driveIndex)) moel@369: using (ManagementObjectCollection dpc = s.Get()) moel@369: foreach (ManagementObject dp in dpc) moel@369: using (ManagementObjectCollection ldc = moel@369: dp.GetRelated("Win32_LogicalDisk")) moel@369: foreach (ManagementBaseObject ld in ldc) moel@369: list.Add(((string)ld["Name"]).TrimEnd(':')); moel@369: } catch { } moel@369: return list.ToArray(); moel@369: } moel@369: moel@324: protected static class NativeMethods { moel@324: private const string KERNEL = "kernel32.dll"; moel@324: moel@324: [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi, moel@324: CharSet = CharSet.Unicode)] moel@324: public static extern IntPtr CreateFile(string fileName, moel@324: AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes, moel@324: CreationMode creationDisposition, FileAttribute flagsAndAttributes, moel@324: IntPtr templateFilehandle); moel@324: moel@324: [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] moel@324: public static extern int CloseHandle(IntPtr handle); moel@324: moel@324: [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] moel@324: [return: MarshalAsAttribute(UnmanagedType.Bool)] moel@324: public static extern bool DeviceIoControl(IntPtr handle, moel@324: DriveCommand command, ref DriveCommandParameter parameter, moel@324: int parameterSize, out DriveSmartReadDataResult result, int resultSize, moel@324: out uint bytesReturned, IntPtr overlapped); moel@324: moel@324: [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] moel@324: [return: MarshalAsAttribute(UnmanagedType.Bool)] moel@324: public static extern bool DeviceIoControl(IntPtr handle, moel@324: DriveCommand command, ref DriveCommandParameter parameter, moel@324: int parameterSize, out DriveSmartReadThresholdsResult result, moel@324: int resultSize, out uint bytesReturned, IntPtr overlapped); moel@324: moel@324: [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] moel@324: [return: MarshalAsAttribute(UnmanagedType.Bool)] moel@324: public static extern bool DeviceIoControl(IntPtr handle, moel@324: DriveCommand command, ref DriveCommandParameter parameter, moel@324: int parameterSize, out DriveCommandResult result, int resultSize, moel@324: out uint bytesReturned, IntPtr overlapped); moel@324: moel@324: [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] moel@324: [return: MarshalAsAttribute(UnmanagedType.Bool)] moel@324: public static extern bool DeviceIoControl(IntPtr handle, moel@324: DriveCommand command, ref DriveCommandParameter parameter, moel@324: int parameterSize, out DriveIdentifyResult result, int resultSize, moel@324: out uint bytesReturned, IntPtr overlapped); moel@324: } moel@324: } moel@324: }