Anyone knows if it is possible to detect a SSD drive using C#? I have seen some suggestions on other sites to try to obtain RPM of a drive, for a SSD this always would be 0. Unfortunately these were just suggestion, an no real examples I can work with. Anyone has a idea how to go from here? [fs]
I've spent several days trying to find something to help you but all I get is people asking the same thing as you. I will of course continue to look into it. Not having an SSD drive doesn't help either.
I keep getting hits on this so I'll post this for now and continue searching Windows 7 detects SSDs by using ATA8-ACS identify word 217: Nominal media rotation rate, with value 0001h as Non-rotating media like solid state devices. But not all SSDs adhere to the ATA8-ACS1 spec section 7.16.7.77, some may need firmware updates
Found these post as well The topic most close by the issue at hand i found here: http://blogs.technet.com/b/keithcombs/archive/2010/08/20/windows-trim-and-other-ssd-mysteries.aspx This guy mentions the same Word 169 to detect TRIM support and Word 217 to detect rotation rate , but unfortunately using a tool that ain't available public Few Linux entries: http://google.com/?q=SSD Detection Word 217
Nominal rotational speed seems to be how to do it. If it's 1 or 0 it's a SSD. So I suggest you guys google for how to execute the ATA IDENTIFY command from c#.
http://stackoverflow.com/questions/5070987/sending-ata-commands-directly-to-device-in-windows This may be of help to you...
I know this thread is a little old but I thought I'd share my solution, I can't guarantee that it'd work on all SSDs (since it's a 'dirty' solution) but it should work on most. Code: static bool DriveIsSSD(string drive_letter) { foreach (ManagementObject obj in new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive").Get()) { if(obj["PNPDeviceID"].ToString().Contains("SSD")) { foreach (ManagementObject partition in obj.GetRelated("Win32_DiskPartition")) foreach (ManagementObject drive in partition.GetRelated("Win32_LogicalDisk")) if (drive["Name"].ToString().StartsWith(drive_letter)) return true; } } return false; } Note that you need to use the System.Management namespace and include System.Management as a reference.
You could also write a temp-file and check, how mutch time it will use. Then you calc MB/Time and if you have more than 200 it its a SSD, else a HDD
If you do not want to use WMI, you can also use DeviceIoControl() to check if Trim is supported. IOCTL_STORAGE_QUERY_PROPERTY http://msdn.microsoft.com/en-us/library/windows/desktop/ff800830(v=vs.85).aspx DEVICE_TRIM_DESCRIPTOR http://msdn.microsoft.com/en-us/library/windows/desktop/ff800829(v=vs.85).aspx
http://emoacht.wordpress.com/2012/11/06/csharp-ssd/ Code: using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; using System.Text; namespace DetectSsd { class Program { // For CreateFile to get handle to drive private const uint GENERIC_READ = 0x80000000; private const uint GENERIC_WRITE = 0x40000000; private const uint FILE_SHARE_READ = 0x00000001; private const uint FILE_SHARE_WRITE = 0x00000002; private const uint OPEN_EXISTING = 3; private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; // CreateFile to get handle to drive [DllImport("kernel32.dll", SetLastError = true)] private static extern SafeFileHandle CreateFileW( [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); // For control codes private const uint FILE_DEVICE_MASS_STORAGE = 0x0000002d; private const uint IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE; private const uint FILE_DEVICE_CONTROLLER = 0x00000004; private const uint IOCTL_SCSI_BASE = FILE_DEVICE_CONTROLLER; private const uint METHOD_BUFFERED = 0; private const uint FILE_ANY_ACCESS = 0; private const uint FILE_READ_ACCESS = 0x00000001; private const uint FILE_WRITE_ACCESS = 0x00000002; private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access) { return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method); } // For DeviceIoControl to check no seek penalty private const uint StorageDeviceSeekPenaltyProperty = 7; private const uint PropertyStandardQuery = 0; [StructLayout(LayoutKind.Sequential)] private struct STORAGE_PROPERTY_QUERY { public uint PropertyId; public uint QueryType; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public byte[] AdditionalParameters; } [StructLayout(LayoutKind.Sequential)] private struct DEVICE_SEEK_PENALTY_DESCRIPTOR { public uint Version; public uint Size; [MarshalAs(UnmanagedType.U1)] public bool IncursSeekPenalty; } // DeviceIoControl to check no seek penalty [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, ref STORAGE_PROPERTY_QUERY lpInBuffer, uint nInBufferSize, ref DEVICE_SEEK_PENALTY_DESCRIPTOR lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); // For DeviceIoControl to check nominal media rotation rate private const uint ATA_FLAGS_DATA_IN = 0x02; [StructLayout(LayoutKind.Sequential)] private struct ATA_PASS_THROUGH_EX { public ushort Length; public ushort AtaFlags; public byte PathId; public byte TargetId; public byte Lun; public byte ReservedAsUchar; public uint DataTransferLength; public uint TimeOutValue; public uint ReservedAsUlong; public IntPtr DataBufferOffset; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] PreviousTaskFile; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] CurrentTaskFile; } [StructLayout(LayoutKind.Sequential)] private struct ATAIdentifyDeviceQuery { public ATA_PASS_THROUGH_EX header; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public ushort[] data; } // DeviceIoControl to check nominal media rotation rate [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, ref ATAIdentifyDeviceQuery lpInBuffer, uint nInBufferSize, ref ATAIdentifyDeviceQuery lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); // For error message private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; [DllImport("kernel32.dll", SetLastError = true)] static extern uint FormatMessage( uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, uint nSize, IntPtr Arguments); static void Main(string[] args) { Console.WriteLine("Input physical drive number:"); string sDrive = "\\\\.\\PhysicalDrive" + Console.ReadLine(); Console.WriteLine( "Select method (0: No seek penalty, 1: Nominal media rotation rate):"); switch (int.Parse(Console.ReadLine())) { case 0: HasNoSeekPenalty(sDrive); break; case 1: HasNominalMediaRotationRate(sDrive); break; } Console.ReadLine(); } // Method for no seek penalty private static void HasNoSeekPenalty(string sDrive) { SafeFileHandle hDrive = CreateFileW( sDrive, 0, // No access to drive FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (hDrive == null || hDrive.IsInvalid) { string message = GetErrorMessage(Marshal.GetLastWin32Error()); Console.WriteLine("CreateFile failed. " + message); } uint IOCTL_STORAGE_QUERY_PROPERTY = CTL_CODE( IOCTL_STORAGE_BASE, 0x500, METHOD_BUFFERED, FILE_ANY_ACCESS); // From winioctl.h STORAGE_PROPERTY_QUERY query_seek_penalty = new STORAGE_PROPERTY_QUERY(); query_seek_penalty.PropertyId = StorageDeviceSeekPenaltyProperty; query_seek_penalty.QueryType = PropertyStandardQuery; DEVICE_SEEK_PENALTY_DESCRIPTOR query_seek_penalty_desc = new DEVICE_SEEK_PENALTY_DESCRIPTOR(); uint returned_query_seek_penalty_size; bool query_seek_penalty_result = DeviceIoControl( hDrive, IOCTL_STORAGE_QUERY_PROPERTY, ref query_seek_penalty, (uint)Marshal.SizeOf(query_seek_penalty), ref query_seek_penalty_desc, (uint)Marshal.SizeOf(query_seek_penalty_desc), out returned_query_seek_penalty_size, IntPtr.Zero); hDrive.Close(); if (query_seek_penalty_result == false) { string message = GetErrorMessage(Marshal.GetLastWin32Error()); Console.WriteLine("DeviceIoControl failed. " + message); } else { if (query_seek_penalty_desc.IncursSeekPenalty == false) { Console.WriteLine("This drive has NO SEEK penalty."); } else { Console.WriteLine("This drive has SEEK penalty."); } } } // Method for nominal media rotation rate // (Administrative privilege is required) private static void HasNominalMediaRotationRate(string sDrive) { SafeFileHandle hDrive = CreateFileW( sDrive, GENERIC_READ | GENERIC_WRITE, // Administrative privilege is required FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (hDrive == null || hDrive.IsInvalid) { string message = GetErrorMessage(Marshal.GetLastWin32Error()); Console.WriteLine("CreateFile failed. " + message); } uint IOCTL_ATA_PASS_THROUGH = CTL_CODE( IOCTL_SCSI_BASE, 0x040b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS); // From ntddscsi.h ATAIdentifyDeviceQuery id_query = new ATAIdentifyDeviceQuery(); id_query.data = new ushort[256]; id_query.header.Length = (ushort)Marshal.SizeOf(id_query.header); id_query.header.AtaFlags = (ushort)ATA_FLAGS_DATA_IN; id_query.header.DataTransferLength = (uint)(id_query.data.Length * 2); // Size of "data" in bytes id_query.header.TimeOutValue = 3; // Sec id_query.header.DataBufferOffset = (IntPtr)Marshal.OffsetOf( typeof(ATAIdentifyDeviceQuery), "data"); id_query.header.PreviousTaskFile = new byte[8]; id_query.header.CurrentTaskFile = new byte[8]; id_query.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE uint retval_size; bool result = DeviceIoControl( hDrive, IOCTL_ATA_PASS_THROUGH, ref id_query, (uint)Marshal.SizeOf(id_query), ref id_query, (uint)Marshal.SizeOf(id_query), out retval_size, IntPtr.Zero); hDrive.Close(); if (result == false) { string message = GetErrorMessage(Marshal.GetLastWin32Error()); Console.WriteLine("DeviceIoControl failed. " + message); } else { // Word index of nominal media rotation rate // (1 means non-rotate device) const int kNominalMediaRotRateWordIndex = 217; if (id_query.data[kNominalMediaRotRateWordIndex] == 1) { Console.WriteLine("This drive is NON-ROTATE device."); } else { Console.WriteLine("This drive is ROTATE device."); } } } // Method for error message private static string GetErrorMessage(int code) { StringBuilder message = new StringBuilder(255); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)code, 0, message, (uint)message.Capacity, IntPtr.Zero); return message.ToString(); } } }