C# 检查硬盘分区是ssd还是hdd
C# detect driver ssd/hdd
来自github的代码,略做了一丢丢修改。
using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security; using System.Security.Principal; using System.Text; using System.Threading.Tasks; /// <summary> /// Class DiskDetectionUtils. /// reference : https://github.com/bitbeans/diskdetector-net /// </summary> public static class DiskDetectionUtils { #region DeviceIoControl (nominal media rotation rate) /// <summary> /// The ata flags data in /// </summary> private const uint AtaFlagsDataIn = 0x02; #endregion /// <summary> /// Check if the application is running as administrator. /// </summary> /// <returns><c>true</c> if the application is running as administrator otherwise, <c>false</c></returns> /// <exception cref="SecurityException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> public static bool IsAdministrator() { var identity = WindowsIdentity.GetCurrent(); if (identity == null) return false; var principal = new WindowsPrincipal(identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } /// <summary> /// Detect a fixed drive by letter. /// </summary> /// <param name="driveName">A valid drive letter.</param> /// <param name="queryType">The QueryType.</param> /// <param name="useFallbackQuery">Use QueryType.SeekPenalty as fallback.</param> /// <returns>A list of DriveInfoExtended.</returns> /// <exception cref="SecurityException">DetectHardwareTypeBySeekPenalty needs administrative access.</exception> /// <exception cref="IOException"></exception> /// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="DriveNotFoundException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="DetectionFailedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static DriveInfoExtended DetectFixedDrive(string driveName, QueryType queryType = QueryType.SeekPenalty, bool useFallbackQuery = true) { var driveInfoExtended = new DriveInfoExtended(); var logicalDrive = new DriveInfo(driveName); if (logicalDrive.DriveType == DriveType.Fixed) { if (logicalDrive.IsReady) { var tmp = new DriveInfoExtended { DriveFormat = logicalDrive.DriveFormat, VolumeLabel = logicalDrive.VolumeLabel, Name = logicalDrive.Name, UncPath = Pathing.GetUNCPath(logicalDrive.Name), DriveType = logicalDrive.DriveType, AvailableFreeSpace = logicalDrive.AvailableFreeSpace, TotalSize = logicalDrive.TotalSize, TotalFreeSpace = logicalDrive.TotalFreeSpace, RootDirectory = logicalDrive.RootDirectory, DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0] }; var driveId = GetDiskId(tmp.DriveLetter); if (driveId != -1) { tmp.Id = driveId; if (queryType == QueryType.SeekPenalty) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { if (IsAdministrator()) { tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId); } else { if (useFallbackQuery) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { throw new SecurityException( "DetectHardwareTypeBySeekPenalty needs administrative access."); } } } if (tmp.HardwareType != HardwareType.Unknown) { driveInfoExtended = tmp; } } } } return driveInfoExtended; } /// <summary> /// Detect all fixed drives. /// </summary> /// <param name="queryType">The QueryType.</param> /// <param name="useFallbackQuery">Use QueryType.SeekPenalty as fallback.</param> /// <returns>A list of DriveInfoExtended.</returns> /// <exception cref="SecurityException">DetectHardwareTypeBySeekPenalty needs administrative access.</exception> /// <exception cref="IOException"></exception> /// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="DriveNotFoundException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="DetectionFailedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static List<DriveInfoExtended> DetectFixedDrives(QueryType queryType = QueryType.SeekPenalty, bool useFallbackQuery = true) { var driveInfoExtended = new List<DriveInfoExtended>(); var logicalDrives = DriveInfo.GetDrives(); foreach (var logicalDrive in logicalDrives) { if (logicalDrive.DriveType == DriveType.Fixed) { if (logicalDrive.IsReady) { var tmp = new DriveInfoExtended { DriveFormat = logicalDrive.DriveFormat, VolumeLabel = logicalDrive.VolumeLabel, Name = logicalDrive.Name, UncPath = Pathing.GetUNCPath(logicalDrive.Name), DriveType = logicalDrive.DriveType, AvailableFreeSpace = logicalDrive.AvailableFreeSpace, TotalSize = logicalDrive.TotalSize, TotalFreeSpace = logicalDrive.TotalFreeSpace, RootDirectory = logicalDrive.RootDirectory, DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0] }; var driveId = GetDiskId(tmp.DriveLetter); if (driveId != -1) { tmp.Id = driveId; if (queryType == QueryType.SeekPenalty) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { if (IsAdministrator()) { tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId); } else { if (useFallbackQuery) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { throw new SecurityException( "DetectHardwareTypeBySeekPenalty needs administrative access."); } } } if (tmp.HardwareType != HardwareType.Unknown) { driveInfoExtended.Add(tmp); } } } } } return driveInfoExtended; } /// <summary> /// Detect a fixed or removable drive. /// </summary> /// <param name="driveName">A valid drive letter.</param> /// <param name="queryType">The QueryType.</param> /// <param name="useFallbackQuery">Use QueryType.SeekPenalty as fallback.</param> /// <returns>A list of DriveInfoExtended.</returns> /// <exception cref="SecurityException">DetectHardwareTypeBySeekPenalty needs administrative access.</exception> /// <exception cref="IOException"></exception> /// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="DriveNotFoundException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="DetectionFailedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static DriveInfoExtended DetectDrive(string driveName, QueryType queryType = QueryType.SeekPenalty, bool useFallbackQuery = true) { var driveInfoExtended = new DriveInfoExtended(); var logicalDrive = new DriveInfo(driveName); if (logicalDrive.DriveType == DriveType.Fixed) { if (logicalDrive.IsReady) { var tmp = new DriveInfoExtended { DriveFormat = logicalDrive.DriveFormat, VolumeLabel = logicalDrive.VolumeLabel, Name = logicalDrive.Name, UncPath = Pathing.GetUNCPath(logicalDrive.Name), DriveType = logicalDrive.DriveType, AvailableFreeSpace = logicalDrive.AvailableFreeSpace, TotalSize = logicalDrive.TotalSize, TotalFreeSpace = logicalDrive.TotalFreeSpace, RootDirectory = logicalDrive.RootDirectory, DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0] }; var driveId = GetDiskId(tmp.DriveLetter); if (driveId != -1) { tmp.Id = driveId; if (queryType == QueryType.SeekPenalty) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { if (IsAdministrator()) { tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId); } else { if (useFallbackQuery) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { throw new SecurityException( "DetectHardwareTypeBySeekPenalty needs administrative access."); } } } driveInfoExtended = tmp; } } } else { if (logicalDrive.IsReady) { var tmp = new DriveInfoExtended { DriveFormat = logicalDrive.DriveFormat, VolumeLabel = logicalDrive.VolumeLabel, Name = logicalDrive.Name, UncPath = Pathing.GetUNCPath(logicalDrive.Name), DriveType = logicalDrive.DriveType, AvailableFreeSpace = logicalDrive.AvailableFreeSpace, TotalSize = logicalDrive.TotalSize, TotalFreeSpace = logicalDrive.TotalFreeSpace, RootDirectory = logicalDrive.RootDirectory, DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0], HardwareType = HardwareType.Unknown, Id = -1 }; driveInfoExtended = tmp; } } return driveInfoExtended; } /// <summary> /// Detect fixed and removable drives. /// </summary> /// <param name="queryType">The QueryType.</param> /// <param name="useFallbackQuery">Use QueryType.SeekPenalty as fallback.</param> /// <returns>A list of DriveInfoExtended.</returns> /// <exception cref="SecurityException">DetectHardwareTypeBySeekPenalty needs administrative access.</exception> /// <exception cref="IOException"></exception> /// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="DriveNotFoundException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="DetectionFailedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static List<DriveInfoExtended> DetectDrives(QueryType queryType = QueryType.SeekPenalty, bool useFallbackQuery = true) { var driveInfoExtended = new List<DriveInfoExtended>(); var logicalDrives = DriveInfo.GetDrives(); foreach (var logicalDrive in logicalDrives) { if (logicalDrive.DriveType == DriveType.Fixed) { if (logicalDrive.IsReady) { var tmp = new DriveInfoExtended { DriveFormat = logicalDrive.DriveFormat, VolumeLabel = logicalDrive.VolumeLabel, Name = logicalDrive.Name, UncPath = Pathing.GetUNCPath(logicalDrive.Name), DriveType = logicalDrive.DriveType, AvailableFreeSpace = logicalDrive.AvailableFreeSpace, TotalSize = logicalDrive.TotalSize, TotalFreeSpace = logicalDrive.TotalFreeSpace, RootDirectory = logicalDrive.RootDirectory, DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0] }; var driveId = GetDiskId(tmp.DriveLetter); if (driveId != -1) { tmp.Id = driveId; if (queryType == QueryType.SeekPenalty) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { if (IsAdministrator()) { tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId); } else { if (useFallbackQuery) { tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId); } else { throw new SecurityException( "DetectHardwareTypeBySeekPenalty needs administrative access."); } } } driveInfoExtended.Add(tmp); } } } else { if (logicalDrive.IsReady) { var tmp = new DriveInfoExtended { DriveFormat = logicalDrive.DriveFormat, VolumeLabel = logicalDrive.VolumeLabel, Name = logicalDrive.Name, UncPath = Pathing.GetUNCPath(logicalDrive.Name), DriveType = logicalDrive.DriveType, AvailableFreeSpace = logicalDrive.AvailableFreeSpace, TotalSize = logicalDrive.TotalSize, TotalFreeSpace = logicalDrive.TotalFreeSpace, RootDirectory = logicalDrive.RootDirectory, DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0], HardwareType = HardwareType.Unknown, Id = -1 }; driveInfoExtended.Add(tmp); } } } return driveInfoExtended; } /// <summary> /// DeviceIoControl to get disk extents /// </summary> /// <param name="hDevice">The h device.</param> /// <param name="dwIoControlCode">The dw io control code.</param> /// <param name="lpInBuffer">The lp in buffer.</param> /// <param name="nInBufferSize">Size of the n in buffer.</param> /// <param name="lpOutBuffer">The lp out buffer.</param> /// <param name="nOutBufferSize">Size of the n out buffer.</param> /// <param name="lpBytesReturned">The lp bytes returned.</param> /// <param name="lpOverlapped">The lp overlapped.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, ref VolumeDiskExtents lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); /// <summary> /// Gets the device ID by drive letter. /// </summary> /// <param name="driveLetter">A valid drive letter.</param> /// <returns>The device ID.</returns> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="Win32Exception"></exception> /// <exception cref="Win32Exception"></exception> /// <exception cref="DetectionFailedException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> private static int GetDiskId(char driveLetter) { var di = new DriveInfo(driveLetter.ToString()); if (di.DriveType != DriveType.Fixed) { throw new DetectionFailedException(string.Format("This drive is not fixed drive: {0}", driveLetter)); } var sDrive = "\\\\.\\" + driveLetter + ":"; var hDrive = CreateFileW( sDrive, 0, // No access to drive FileShareRead | FileShareWrite, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero); if (hDrive == null || hDrive.IsInvalid) { int lastError = Marshal.GetLastWin32Error(); throw new DetectionFailedException(string.Format("Could not detect Disk Id of {0}", driveLetter), new Win32Exception(lastError) ); } var ioctlVolumeGetVolumeDiskExtents = CTL_CODE( IoctlVolumeBase, 0, MethodBuffered, FileAnyAccess); // From winioctl.h var queryDiskExtents = new VolumeDiskExtents(); uint returnedQueryDiskExtentsSize; var queryDiskExtentsResult = DeviceIoControl( hDrive, ioctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, ref queryDiskExtents, (uint)Marshal.SizeOf(queryDiskExtents), out returnedQueryDiskExtentsSize, IntPtr.Zero); hDrive.Close(); if (!queryDiskExtentsResult) { int lastError = Marshal.GetLastWin32Error(); const int ERROR_MORE_DATA = 234; //(0xEA) More data is available. if (lastError != ERROR_MORE_DATA || (queryDiskExtents.Extents.Length < 1) // We need at least 1 ) { throw new DetectionFailedException(string.Format("Could not detect Disk Id of {0}", driveLetter), new Win32Exception(lastError) ); } } return (int)queryDiskExtents.Extents[0].DiskNumber; } /// <summary> /// Detect the HardwareType by SeekPenalty. /// </summary> /// <param name="driveLetter">A valid drive letter.</param> /// <returns>The detected HardwareType.</returns> public static HardwareType DetectHardwareTypeBySeekPenalty(char driveLetter) { try { return DetectHardwareTypeBySeekPenalty(GetDiskId(driveLetter)); } catch (DetectionFailedException) { return HardwareType.Unknown; } } /// <summary> /// Detect the HardwareType by SeekPenalty. /// </summary> /// <param name="driveId">A valid drive Id.</param> /// <returns>The detected HardwareType.</returns> public static HardwareType DetectHardwareTypeBySeekPenalty(int driveId) { var physicalDriveName = "\\\\.\\PhysicalDrive" + driveId; try { return HasDriveSeekPenalty(physicalDriveName) ? HardwareType.Hdd : HardwareType.Ssd; } catch (DetectionFailedException) { return HardwareType.Unknown; } } /// <summary> /// Detect the HardwareType by RotationRate. /// </summary> /// <param name="driveLetter">A valid drive letter.</param> /// <returns>The detected HardwareType.</returns> public static HardwareType DetectHardwareTypeByRotationRate(char driveLetter) { try { return DetectHardwareTypeByRotationRate(GetDiskId(driveLetter)); } catch (DetectionFailedException) { return HardwareType.Unknown; } } /// <summary> /// Detect the HardwareType by RotationRate. /// </summary> /// <param name="driveId">A valid drive Id.</param> /// <returns>The detected HardwareType.</returns> /// <remarks>Administrative privilege is required!</remarks> public static HardwareType DetectHardwareTypeByRotationRate(int driveId) { var physicalDriveName = "\\\\.\\PhysicalDrive" + driveId; try { return HasDriveNominalMediaRotationRate(physicalDriveName) ? HardwareType.Hdd : HardwareType.Ssd; } catch (DetectionFailedException) { return HardwareType.Unknown; } } /// <summary> /// ws the net get connection. /// </summary> /// <param name="localName">Name of the local.</param> /// <param name="remoteName">Name of the remote.</param> /// <param name="length">The length.</param> /// <returns>System.Int32.</returns> [DllImport("mpr.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int WNetGetConnection([MarshalAs(UnmanagedType.LPTStr)] string localName, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName, ref int length); /// <summary> /// Strings to unc. /// </summary> /// <param name="path">The path.</param> /// <returns>System.Object.</returns> private static object strToUnc(string path) { // This sample code assumes you currently have a drive mapped to p: // Find out what remote device a local mapping is to int rc = 0; // Size for the buffer we will use int bsize = 200; // Create a new stringbuilder, pre-sized as above StringBuilder rname = new StringBuilder(bsize); // Call the function rc = WNetGetConnection("Z:", rname, ref bsize); //https://stackoverflow.com/questions/1088752/how-to-programmatically-discover-mapped-network-drives-on-system-and-their-serve //http://www.pinvoke.net/default.aspx/mpr/WNetGetConnection.html int length = 255; /*2250 (0x8CA) This network connection does not exist. 1200 (0x4B0) The specified device name is invalid.*/ System.Text.StringBuilder UNC = new System.Text.StringBuilder(length); int q = WNetGetConnection("Z:", UNC, ref length); return UNC.ToString(); } //to get the UNC-Path of a network-drive use something like: /// <summary> /// CreateFile to get handle to drive. /// </summary> /// <param name="lpFileName">Name of the lp file.</param> /// <param name="dwDesiredAccess">The dw desired access.</param> /// <param name="dwShareMode">The dw share mode.</param> /// <param name="lpSecurityAttributes">The lp security attributes.</param> /// <param name="dwCreationDisposition">The dw creation disposition.</param> /// <param name="dwFlagsAndAttributes">The dw flags and attributes.</param> /// <param name="hTemplateFile">The h template file.</param> /// <returns>SafeFileHandle.</returns> [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); /// <summary> /// Controls the code. /// </summary> /// <param name="DeviceType">Type of the device.</param> /// <param name="Function">The function.</param> /// <param name="Method">The method.</param> /// <param name="Access">The access.</param> /// <returns>System.UInt32.</returns> private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access) { return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method); } /// <summary> /// DeviceIoControl to check no seek penalty. /// </summary> /// <param name="hDevice">The h device.</param> /// <param name="dwIoControlCode">The dw io control code.</param> /// <param name="lpInBuffer">The lp in buffer.</param> /// <param name="nInBufferSize">Size of the n in buffer.</param> /// <param name="lpOutBuffer">The lp out buffer.</param> /// <param name="nOutBufferSize">Size of the n out buffer.</param> /// <param name="lpBytesReturned">The lp bytes returned.</param> /// <param name="lpOverlapped">The lp overlapped.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, ref StoragePropertyQuery lpInBuffer, uint nInBufferSize, ref DeviceSeekPenaltyDescriptor lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); /// <summary> /// DeviceIoControl to check nominal media rotation rate. /// </summary> /// <param name="hDevice">The h device.</param> /// <param name="dwIoControlCode">The dw io control code.</param> /// <param name="lpInBuffer">The lp in buffer.</param> /// <param name="nInBufferSize">Size of the n in buffer.</param> /// <param name="lpOutBuffer">The lp out buffer.</param> /// <param name="nOutBufferSize">Size of the n out buffer.</param> /// <param name="lpBytesReturned">The lp bytes returned.</param> /// <param name="lpOverlapped">The lp overlapped.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> [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); /// <summary> /// Check if the drive has a seek penalty. /// </summary> /// <param name="physicalDriveName">A valid physicalDriveName.</param> /// <returns><c> true </c> if the drive has a seek penalty otherwise, <c> false </c></returns> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="Win32Exception"></exception> /// <exception cref="Win32Exception"></exception> /// <exception cref="DetectionFailedException"></exception> /// <remarks>Administrative privilege is required!</remarks> private static bool HasDriveSeekPenalty(string physicalDriveName) { var hDrive = CreateFileW( physicalDriveName, 0, // No access to drive FileShareRead | FileShareWrite, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero); if (hDrive == null || hDrive.IsInvalid) { int lastError = Marshal.GetLastWin32Error(); throw new DetectionFailedException(string.Format("Could not detect SeekPenalty of {0}", physicalDriveName), new Win32Exception(lastError) ); } var ioctlStorageQueryProperty = CTL_CODE( IoctlStorageBase, 0x500, MethodBuffered, FileAnyAccess); // From winioctl.h var querySeekPenalty = new StoragePropertyQuery { PropertyId = StorageDeviceSeekPenaltyProperty, QueryType = PropertyStandardQuery }; var querySeekPenaltyDesc = new DeviceSeekPenaltyDescriptor(); uint returnedQuerySeekPenaltySize; var querySeekPenaltyResult = DeviceIoControl( hDrive, ioctlStorageQueryProperty, ref querySeekPenalty, (uint)Marshal.SizeOf(querySeekPenalty), ref querySeekPenaltyDesc, (uint)Marshal.SizeOf(querySeekPenaltyDesc), out returnedQuerySeekPenaltySize, IntPtr.Zero); hDrive.Close(); if (querySeekPenaltyResult == false) { int lastError = Marshal.GetLastWin32Error(); throw new DetectionFailedException(string.Format("Could not detect SeekPenalty of {0}", physicalDriveName), new Win32Exception(lastError) ); } if (querySeekPenaltyDesc.IncursSeekPenalty == false) { //This drive has NO SEEK penalty return false; } //This drive has SEEK penalty return true; } /// <summary> /// Check if the drive has a nominal media rotation rate. /// </summary> /// <param name="physicalDriveName">A valid physicalDriveName.</param> /// <returns><c> true </c> if the drive has a media rotation rate otherwise, <c> false </c></returns> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="BluePrint.Common.Utility.DetectionFailedException"></exception> /// <exception cref="Win32Exception"></exception> /// <exception cref="Win32Exception"></exception> /// <exception cref="DetectionFailedException"></exception> /// <remarks>Administrative privilege is required!</remarks> private static bool HasDriveNominalMediaRotationRate(string physicalDriveName) { var hDrive = CreateFileW( physicalDriveName, GenericRead | GenericWrite, // Administrative privilege is required FileShareRead | FileShareWrite, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero); if (hDrive == null || hDrive.IsInvalid) { int lastError = Marshal.GetLastWin32Error(); throw new DetectionFailedException(string.Format("Could not detect NominalMediaRotationRate of {0}", physicalDriveName), new Win32Exception(lastError) ); } var ioctlAtaPassThrough = CTL_CODE( IoctlScsiBase, 0x040b, MethodBuffered, FileReadAccess | FileWriteAccess); // From ntddscsi.h var idQuery = new AtaIdentifyDeviceQuery { data = new ushort[256] }; idQuery.header.Length = (ushort)Marshal.SizeOf(idQuery.header); idQuery.header.AtaFlags = (ushort)AtaFlagsDataIn; idQuery.header.DataTransferLength = (uint)(idQuery.data.Length * 2); // Size of "data" in bytes idQuery.header.TimeOutValue = 3; // Sec idQuery.header.DataBufferOffset = Marshal.OffsetOf( typeof(AtaIdentifyDeviceQuery), "data"); idQuery.header.PreviousTaskFile = new byte[8]; idQuery.header.CurrentTaskFile = new byte[8]; idQuery.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE uint retvalSize; var result = DeviceIoControl( hDrive, ioctlAtaPassThrough, ref idQuery, (uint)Marshal.SizeOf(idQuery), ref idQuery, (uint)Marshal.SizeOf(idQuery), out retvalSize, IntPtr.Zero); hDrive.Close(); if (result == false) { int lastError = Marshal.GetLastWin32Error(); throw new DetectionFailedException(string.Format("Could not detect NominalMediaRotationRate of {0}", physicalDriveName), new Win32Exception(lastError) ); } // Word index of nominal media rotation rate // (1 means non-rotate device) const int kNominalMediaRotRateWordIndex = 217; if (idQuery.data[kNominalMediaRotRateWordIndex] == 1) { return false; } return true; } /// <summary> /// For DeviceIoControl to get disk extents /// </summary> [StructLayout(LayoutKind.Sequential)] private struct DiskExtent { /// <summary> /// The disk number /// </summary> public readonly uint DiskNumber; /// <summary> /// The starting offset /// </summary> public readonly long StartingOffset; /// <summary> /// The extent length /// </summary> public readonly long ExtentLength; } /// <summary> /// Struct VolumeDiskExtents /// </summary> [StructLayout(LayoutKind.Sequential)] private struct VolumeDiskExtents { /// <summary> /// The number of disk extents /// </summary> public readonly uint NumberOfDiskExtents; /// <summary> /// The extents /// </summary> [MarshalAs(UnmanagedType.ByValArray)] public readonly DiskExtent[] Extents; } /// <summary> /// Struct StoragePropertyQuery /// </summary> [StructLayout(LayoutKind.Sequential)] private struct StoragePropertyQuery { /// <summary> /// The property identifier /// </summary> public uint PropertyId; /// <summary> /// The query type /// </summary> public uint QueryType; /// <summary> /// The additional parameters /// </summary> [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public readonly byte[] AdditionalParameters; } /// <summary> /// Struct DeviceSeekPenaltyDescriptor /// </summary> [StructLayout(LayoutKind.Sequential)] private struct DeviceSeekPenaltyDescriptor { /// <summary> /// The version /// </summary> public readonly uint Version; /// <summary> /// The size /// </summary> public readonly uint Size; /// <summary> /// The incurs seek penalty /// </summary> [MarshalAs(UnmanagedType.U1)] public readonly bool IncursSeekPenalty; } /// <summary> /// Struct AtaPassThroughEx /// </summary> [StructLayout(LayoutKind.Sequential)] private struct AtaPassThroughEx { /// <summary> /// The length /// </summary> public ushort Length; /// <summary> /// The ata flags /// </summary> public ushort AtaFlags; /// <summary> /// The path identifier /// </summary> public readonly byte PathId; /// <summary> /// The target identifier /// </summary> public readonly byte TargetId; /// <summary> /// The lun /// </summary> public readonly byte Lun; /// <summary> /// The reserved as uchar /// </summary> public readonly byte ReservedAsUchar; /// <summary> /// The data transfer length /// </summary> public uint DataTransferLength; /// <summary> /// The time out value /// </summary> public uint TimeOutValue; /// <summary> /// The reserved as ulong /// </summary> public readonly uint ReservedAsUlong; /// <summary> /// The data buffer offset /// </summary> public IntPtr DataBufferOffset; /// <summary> /// The previous task file /// </summary> [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] PreviousTaskFile; /// <summary> /// The current task file /// </summary> [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] CurrentTaskFile; } /// <summary> /// Struct AtaIdentifyDeviceQuery /// </summary> [StructLayout(LayoutKind.Sequential)] private struct AtaIdentifyDeviceQuery { /// <summary> /// The header /// </summary> public AtaPassThroughEx header; /// <summary> /// The data /// </summary> [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public ushort[] data; } #region CreateFile (get handle to drive) /// <summary> /// The generic read /// </summary> private const uint GenericRead = 0x80000000; /// <summary> /// The generic write /// </summary> private const uint GenericWrite = 0x40000000; /// <summary> /// The file share read /// </summary> private const uint FileShareRead = 0x00000001; /// <summary> /// The file share write /// </summary> private const uint FileShareWrite = 0x00000002; /// <summary> /// The open existing /// </summary> private const uint OpenExisting = 3; /// <summary> /// The file attribute normal /// </summary> private const uint FileAttributeNormal = 0x00000080; #endregion #region Control Codes /// <summary> /// The file device mass storage /// </summary> private const uint FileDeviceMassStorage = 0x0000002d; /// <summary> /// The ioctl storage base /// </summary> private const uint IoctlStorageBase = FileDeviceMassStorage; /// <summary> /// The file device controller /// </summary> private const uint FileDeviceController = 0x00000004; /// <summary> /// The ioctl scsi base /// </summary> private const uint IoctlScsiBase = FileDeviceController; /// <summary> /// The method buffered /// </summary> private const uint MethodBuffered = 0; /// <summary> /// The file any access /// </summary> private const uint FileAnyAccess = 0; /// <summary> /// The file read access /// </summary> private const uint FileReadAccess = 0x00000001; /// <summary> /// The file write access /// </summary> private const uint FileWriteAccess = 0x00000002; /// <summary> /// The ioctl volume base /// </summary> private const uint IoctlVolumeBase = 0x00000056; #endregion #region DeviceIoControl (seek penalty) /// <summary> /// The storage device seek penalty property /// </summary> private const uint StorageDeviceSeekPenaltyProperty = 7; /// <summary> /// The property standard query /// </summary> private const uint PropertyStandardQuery = 0; #endregion } /// <summary> /// Class NativeMethods. /// </summary> public partial class NativeMethods { /// <summary> /// The type of structure that the function stores in the buffer. /// </summary> public enum InfoLevel { /// <summary> /// The function stores a <see cref="UNIVERSAL_NAME_INFO" /> structure in the /// buffer. /// </summary> UniversalName = 1, /// <summary> /// The function stores a <c>REMOTE_NAME_INFO</c> structure in the buffer. /// </summary> RemoteName = 2 } /// <summary> /// ws the name of the net get universal. /// </summary> /// <param name="lpLocalPath">The lp local path.</param> /// <param name="dwInfoLevel">The dw information level.</param> /// <param name="lpBuffer">The lp buffer.</param> /// <param name="lpBufferSize">Size of the lp buffer.</param> /// <returns>System.Int32.</returns> [DllImport("mpr.dll", CharSet = CharSet.Auto)] public static extern int WNetGetUniversalName( string lpLocalPath, InfoLevel dwInfoLevel, ref UNIVERSAL_NAME_INFO lpBuffer, ref int lpBufferSize); /// <summary> /// ws the name of the net get universal. /// </summary> /// <param name="lpLocalPath">The lp local path.</param> /// <param name="dwInfoLevel">The dw information level.</param> /// <param name="lpBuffer">The lp buffer.</param> /// <param name="lpBufferSize">Size of the lp buffer.</param> /// <returns>System.Int32.</returns> [DllImport("mpr.dll", CharSet = CharSet.Auto)] public static extern int WNetGetUniversalName( string lpLocalPath, InfoLevel dwInfoLevel, IntPtr lpBuffer, ref int lpBufferSize); /// <summary> /// The <see cref="UNIVERSAL_NAME_INFO" /> structure contains a pointer to a /// Universal Naming Convention (UNC) name string for a network resource. /// </summary> [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct UNIVERSAL_NAME_INFO { /// <summary> /// Pointer to the null-terminated UNC name string that identifies a /// network resource. /// </summary> [MarshalAs(UnmanagedType.LPTStr)] public string lpUniversalName; } } /// <summary> /// Class DetectionFailedException. /// Implements the <see cref="System.Exception" /> /// </summary> /// <seealso cref="System.Exception" /> public class DetectionFailedException : Exception { /// <summary> /// Initializes a new instance of the <see cref="DetectionFailedException"/> class. /// </summary> public DetectionFailedException() { } /// <summary> /// Initializes a new instance of the <see cref="DetectionFailedException"/> class. /// </summary> /// <param name="message">描述错误的消息。</param> public DetectionFailedException(string message) : base(message) { } /// <summary> /// Initializes a new instance of the <see cref="DetectionFailedException"/> class. /// </summary> /// <param name="message">The message.</param> /// <param name="inner">The inner.</param> public DetectionFailedException(string message, Exception inner) : base(message, inner) { } } /// <summary> /// Class DriveInfoExtended. /// </summary> public class DriveInfoExtended { /// <summary> /// Gets or sets the name. /// </summary> /// <value>The name.</value> public string Name { get; set; } /// <summary> /// Gets or sets the drive letter. /// </summary> /// <value>The drive letter.</value> public char DriveLetter { get; set; } /// <summary> /// Gets or sets the type of the drive. /// </summary> /// <value>The type of the drive.</value> public DriveType DriveType { get; set; } /// <summary> /// Gets or sets the identifier. /// </summary> /// <value>The identifier.</value> public int Id { get; set; } /// <summary> /// Gets or sets the volume label. /// </summary> /// <value>The volume label.</value> public string VolumeLabel { get; set; } /// <summary> /// Gets or sets the drive format. /// </summary> /// <value>The drive format.</value> public string DriveFormat { get; set; } /// <summary> /// Gets or sets the total free space. /// </summary> /// <value>The total free space.</value> public long TotalFreeSpace { get; set; } /// <summary> /// Gets or sets the total size. /// </summary> /// <value>The total size.</value> public long TotalSize { get; set; } /// <summary> /// Gets or sets the available free space. /// </summary> /// <value>The available free space.</value> public long AvailableFreeSpace { get; set; } /// <summary> /// Gets or sets the type of the hardware. /// </summary> /// <value>The type of the hardware.</value> public HardwareType HardwareType { get; set; } /// <summary> /// Gets or sets the root directory. /// </summary> /// <value>The root directory.</value> public DirectoryInfo RootDirectory { get; set; } /// <summary> /// Gets or sets the unc path. /// </summary> /// <value>The unc path.</value> public string UncPath { get; set; } } /// <summary> /// Possible HardwareTypes. /// </summary> public enum HardwareType { /// <summary> /// Unknown hardware type. /// </summary> Unknown, /// <summary> /// Hard Disk Drive. /// </summary> Hdd, /// <summary> /// Solid State Drive. /// </summary> Ssd } /// <summary> /// Possible QueryTypes. /// </summary> public enum QueryType { /// <summary> /// Detect the HardwareType by SeekPenalty. /// </summary> SeekPenalty, /// <summary> /// Detect the HardwareType by RotationRate. /// </summary> RotationRate } /// <summary> /// Class Pathing. /// </summary> internal static class Pathing { [DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int WNetGetConnection( [MarshalAs(UnmanagedType.LPTStr)] string localName, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName, ref int length); /// <summary> /// Given a path, returns the UNC path or the original. (No exceptions /// are raised by this function directly). For example, "P:\2008-02-29" /// might return: "\\networkserver\Shares\Photos\2008-02-09" /// http://www.wiredprairie.us/blog/index.php/archives/22 /// </summary> /// <param name="originalPath">The path to convert to a UNC Path</param> /// <returns> /// A UNC path. If a network drive letter is specified, the /// drive letter is converted to a UNC or network path. If the /// originalPath cannot be converted, it is returned unchanged. /// </returns> public static string GetUNCPath(string originalPath) { var sb = new StringBuilder(512); var size = sb.Capacity; // look for the {LETTER}: combination ... if (originalPath.Length > 2 && originalPath[1] == ':') { // don't use char.IsLetter here - as that can be misleading // the only valid drive letters are a-z && A-Z. var c = originalPath[0]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { var error = WNetGetConnection(originalPath.Substring(0, 2), sb, ref size); if (error == 0) { var dir = new DirectoryInfo(originalPath); var path = Path.GetFullPath(originalPath) .Substring(Path.GetPathRoot(originalPath).Length); return Path.Combine(sb.ToString().TrimEnd(), path); } } } return originalPath; } }
调用接口DiskDetectionUtils.DetectDrive即可,第一个参数是a-z的字符。比如:
var ext = DiskDetectionUtils.DetectDrive(targetDirectory.Substring(0, 1));
if(ext.HardwareType != HardwareType.Ssd)
{
// do sht.
}