
1. 功能说明
- 根据盘符获取对应的物理磁盘路径(如
C:
→\\.\PhysicalDrive0
) - 支持所有盘符(A-Z)
- 使用 Windows API 直接访问磁盘设备
- 自动处理卷跨越多个物理磁盘的情况
- 完善的错误处理和资源管理
2. 实现代码
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace ComPE_ToolBox
{
/// <summary>
/// 磁盘工具类 - 提供磁盘相关操作功能
/// </summary>
public static class DiskUtility
{
private const uint ERROR_MORE_DATA = 234;
private const uint IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x560000;
/// <summary>
/// 获取逻辑驱动器对应的物理磁盘路径
/// </summary>
/// <param name="driveLetter">盘符字母(A-Z)</param>
/// <returns>物理磁盘路径(如 \\.\PhysicalDrive0)</returns>
/// <exception cref="ArgumentException">无效盘符</exception>
/// <exception cref="Win32Exception">Windows API错误</exception>
public static string GetPhysicalDrivePath(char driveLetter)
{
char upperLetter = char.ToUpper(driveLetter);
if (upperLetter < 'A' || upperLetter > 'Z')
throw new ArgumentException("盘符必须是A-Z之间的字母", nameof(driveLetter));
string volumePath = $@"\\.\{upperLetter}:";
using (SafeFileHandle volumeHandle = CreateFile(
volumePath,
FileAccess.Read,
FileShare.Read | FileShare.Write,
IntPtr.Zero,
FileMode.Open,
FileAttributes.Normal,
IntPtr.Zero))
{
if (volumeHandle.IsInvalid)
throw new Win32Exception(Marshal.GetLastWin32Error(), $"无法访问卷 '{upperLetter}:'");
// 首次尝试获取磁盘扩展信息
if (TryGetDiskExtents(volumeHandle, out VOLUME_DISK_EXTENTS diskExtents))
return FormatDrivePath(diskExtents);
// 处理需要更大缓冲区的情况
return HandleLargeBufferCase(volumeHandle);
}
}
private static bool TryGetDiskExtents(SafeFileHandle handle, out VOLUME_DISK_EXTENTS diskExtents)
{
diskExtents = default;
int bytesReturned;
return DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
IntPtr.Zero,
0,
out diskExtents,
Marshal.SizeOf<VOLUME_DISK_EXTENTS>(),
out bytesReturned,
IntPtr.Zero);
}
private static string HandleLargeBufferCase(SafeFileHandle handle)
{
int error = Marshal.GetLastWin32Error();
if (error != ERROR_MORE_DATA)
throw new Win32Exception(error, "获取磁盘扩展信息失败");
// 获取所需缓冲区大小
int bytesReturned;
VOLUME_DISK_EXTENTS_HEADER header;
if (!DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
IntPtr.Zero,
0,
out header,
Marshal.SizeOf<VOLUME_DISK_EXTENTS_HEADER>(),
out bytesReturned,
IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error(), "获取磁盘扩展头信息失败");
// 分配动态缓冲区
int bufferSize = Marshal.SizeOf<VOLUME_DISK_EXTENTS_HEADER>() +
Marshal.SizeOf<DISK_EXTENT>() * (int)header.NumberOfDiskExtents;
IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
try
{
if (!DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
IntPtr.Zero,
0,
buffer,
bufferSize,
out bytesReturned,
IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error(), "获取完整磁盘扩展信息失败");
var diskExtents = Marshal.PtrToStructure<VOLUME_DISK_EXTENTS>(buffer);
return FormatDrivePath(diskExtents);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
private static string FormatDrivePath(VOLUME_DISK_EXTENTS diskExtents)
{
return diskExtents.NumberOfDiskExtents == 0 ?
null :
$@"\\.\PhysicalDrive{diskExtents.Extents[0].DiskNumber}";
}
#region Win32 API 定义
[StructLayout(LayoutKind.Sequential)]
private struct DISK_EXTENT
{
public uint DiskNumber;
public long StartingOffset;
public long ExtentLength;
}
[StructLayout(LayoutKind.Sequential)]
private struct VOLUME_DISK_EXTENTS_HEADER
{
public uint NumberOfDiskExtents;
}
[StructLayout(LayoutKind.Sequential)]
private struct VOLUME_DISK_EXTENTS
{
public uint NumberOfDiskExtents;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public DISK_EXTENT[] Extents;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
int nInBufferSize,
out VOLUME_DISK_EXTENTS lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
int nInBufferSize,
out VOLUME_DISK_EXTENTS_HEADER lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
#endregion
}
}
3. 使用示例
try
{
// 获取C盘对应的物理磁盘路径
string drivePath = DiskUtility.GetPhysicalDrivePath('C');
Console.WriteLine($"C盘物理路径: {drivePath}");
// 获取D盘对应的物理磁盘路径
drivePath = DiskUtility.GetPhysicalDrivePath('D');
Console.WriteLine($"D盘物理路径: {drivePath}");
}
catch (Exception ex)
{
Console.WriteLine($"操作失败: {ex.Message}");
}
// 批量获取所有驱动器物理路径
for (char drive = 'A'; drive <= 'Z'; drive++)
{
try
{
string path = DiskUtility.GetPhysicalDrivePath(drive);
if (path != null)
Console.WriteLine($"{drive}: => {path}");
}
catch (Win32Exception ex)
{
Console.WriteLine($"{drive}: 访问错误 [0x{ex.ErrorCode:X8}]");
}
}
4. 输出示例
输入盘符 | 输出示例 | 说明 |
---|---|---|
C | \\.\PhysicalDrive0 | 系统盘通常对应第一个物理磁盘 |
D | \\.\PhysicalDrive1 | 第二个物理磁盘 |
E | \\.\PhysicalDrive2 | 第三个物理磁盘 |
A | null | 软驱不存在时返回null |
5. 技术参数
项目 | 要求 |
---|---|
操作系统 | Windows Vista 及以上 |
权限要求 | 读取物理磁盘信息:标准用户权限 写入物理磁盘:管理员权限 |
依赖项 | System.Runtime.InteropServices Microsoft.Win32.SafeHandles |
异常处理 | ArgumentException:无效盘符 Win32Exception:API调用错误 IOException:磁盘访问错误 |
6. 注意事项
- 需要以管理员权限运行程序才能访问物理磁盘设备
- 如果卷跨越多个物理磁盘,只返回第一个磁盘的编号
- 支持Windows NT 6.0+(Vista及更高版本)
- 不需要WMI服务,直接使用内核级API
- 使用
SafeFileHandle
确保资源正确释放
非常感谢ComPE项目组提供的贡献
广告:
© 版权声明
THE END
暂无评论内容