通过Win API方式,根据磁盘符号查找磁盘路径

通过Win API方式,根据磁盘符号查找磁盘路径

通过Win API方式,根据磁盘符号查找磁盘路径-云港网络
通过Win API方式,根据磁盘符号查找磁盘路径
此内容为免费资源,请登录后查看
0
持续更新
下载不限速
永久免费
免费资源

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
喜欢就支持一下吧
点赞6打赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容