验证硬盘 SSD 类型
var letter = "D";
var ext = DiskDetectionUtils.DetectDrive(letter);
if (ext.HardwareType != HardwareType.Ssd)
{
XtraMessageBox.Show("The drive letter " + letter + " is not SSD, please check it again!", "Error");
}
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;
}
}

浙公网安备 33010602011771号