Fixed Issue 651.
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/.
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>
14 using System.Collections.Generic;
15 using System.Management;
16 using System.Runtime.InteropServices;
18 namespace OpenHardwareMonitor.Hardware.HDD {
20 internal class WindowsSmart : ISmart {
22 protected enum AccessMode : uint {
30 protected enum ShareMode : uint {
37 protected enum CreationMode : uint {
46 protected enum FileAttribute : uint {
47 Readonly = 0x00000001,
50 Directory = 0x00000010,
54 Temporary = 0x00000100,
55 SparseFile = 0x00000200,
56 ReparsePoint = 0x00000400,
57 Compressed = 0x00000800,
59 NotContentIndexed = 0x00002000,
60 Encrypted = 0x00004000,
63 protected enum DriveCommand : uint {
64 GetVersion = 0x00074080,
65 SendDriveCommand = 0x0007c084,
66 ReceiveDriveData = 0x0007c088
69 protected enum RegisterCommand : byte {
71 /// SMART data requested.
76 /// Identify data is requested.
81 protected enum RegisterFeature : byte {
88 /// Read SMART thresholds.
90 SmartReadThresholds = 0xD1, /* obsolete */
93 /// Autosave SMART data.
98 /// Save SMART attributes.
100 SmartSaveAttr = 0xD3,
103 /// Set SMART to offline immediately.
105 SmartImmediateOffline = 0xD4,
115 SmartWriteLog = 0xD6,
118 /// Write SMART thresholds.
120 SmartWriteThresholds = 0xD7, /* obsolete */
125 SmartEnableOperations = 0xD8,
130 SmartDisableOperations = 0xD9,
133 /// Get SMART status.
138 /// Set SMART to offline automatically.
140 SmartAutoOffline = 0xDB, /* obsolete */
143 [StructLayout(LayoutKind.Sequential, Pack = 1)]
144 protected struct CommandBlockRegisters {
145 public RegisterFeature Features;
146 public byte SectorCount;
151 public RegisterCommand Command;
152 public byte Reserved;
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;
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;
172 [StructLayout(LayoutKind.Sequential, Pack = 1)]
173 protected struct DriveCommandResult {
174 public uint BufferSize;
175 public DriverStatus DriverStatus;
178 [StructLayout(LayoutKind.Sequential, Pack = 1)]
179 protected struct DriveSmartReadDataResult {
180 public uint BufferSize;
181 public DriverStatus DriverStatus;
183 public byte Reserved;
184 [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
185 public DriveAttributeValue[] Attributes;
188 [StructLayout(LayoutKind.Sequential, Pack = 1)]
189 protected struct DriveSmartReadThresholdsResult {
190 public uint BufferSize;
191 public DriverStatus DriverStatus;
193 public byte Reserved;
194 [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DRIVE_ATTRIBUTES)]
195 public DriveThresholdValue[] Thresholds;
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)]
228 [StructLayout(LayoutKind.Sequential, Pack = 1)]
229 protected struct DriveIdentifyResult {
230 public uint BufferSize;
231 public DriverStatus DriverStatus;
232 public Identify Identify;
235 public IntPtr InvalidHandle { get { return (IntPtr)(-1); } }
237 private const byte SMART_LBA_MID = 0x4F;
238 private const byte SMART_LBA_HI = 0xC2;
240 private const int MAX_DRIVE_ATTRIBUTES = 512;
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,
249 public bool EnableSmart(IntPtr handle, int driveNumber) {
250 DriveCommandParameter parameter = new DriveCommandParameter();
251 DriveCommandResult result;
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;
260 return NativeMethods.DeviceIoControl(handle, DriveCommand.SendDriveCommand,
261 ref parameter, Marshal.SizeOf(typeof(DriveCommandParameter)), out result,
262 Marshal.SizeOf(typeof(DriveCommandResult)), out bytesReturned,
266 public DriveAttributeValue[] ReadSmartData(IntPtr handle, int driveNumber) {
267 DriveCommandParameter parameter = new DriveCommandParameter();
268 DriveSmartReadDataResult result;
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;
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);
282 return (isValid) ? result.Attributes : new DriveAttributeValue[0];
285 public DriveThresholdValue[] ReadSmartThresholds(IntPtr handle,
288 DriveCommandParameter parameter = new DriveCommandParameter();
289 DriveSmartReadThresholdsResult result;
290 uint bytesReturned = 0;
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;
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);
303 return (isValid) ? result.Thresholds : new DriveThresholdValue[0];
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];
312 return new string(chars).Trim(new char[] { ' ', '\0' });
315 public bool ReadNameAndFirmwareRevision(IntPtr handle, int driveNumber,
316 out string name, out string firmwareRevision)
318 DriveCommandParameter parameter = new DriveCommandParameter();
319 DriveIdentifyResult result;
322 parameter.DriveNumber = (byte)driveNumber;
323 parameter.Registers.Command = RegisterCommand.IdCmd;
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);
332 firmwareRevision = null;
336 name = GetString(result.Identify.ModelNumber);
337 firmwareRevision = GetString(result.Identify.FirmwareRevision);
341 public void CloseHandle(IntPtr handle) {
342 NativeMethods.CloseHandle(handle);
345 public string[] GetLogicalDrives(int driveIndex) {
346 List<string> list = new List<string>();
348 using (ManagementObjectSearcher s = new ManagementObjectSearcher(
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(':'));
359 return list.ToArray();
362 protected static class NativeMethods {
363 private const string KERNEL = "kernel32.dll";
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);
372 [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
373 public static extern int CloseHandle(IntPtr handle);
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);
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);
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);
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);