You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Windows Server 2019中通过.NET获取磁盘序列号

在Windows Server 2019中用.NET获取磁盘序列号的解决方案

我明白你遇到的问题——在Windows Server 2019里,原来依赖的Win32_DiskDriveWin32_PhysicalMedia WMI类无法返回磁盘序列号,确实挺头疼的。下面给你几个经过验证的.NET实现方案:

方案一:使用存储WMI提供程序(MSFT_PhysicalDisk类)

Windows Server 2012及以后的版本引入了新的存储WMI提供程序,MSFT_PhysicalDisk类(位于root/Microsoft/Windows/Storage命名空间)能更可靠地获取磁盘硬件信息,包括序列号,这也是微软推荐的现代存储信息获取方式。

C# 代码示例

using System.Management;

public static string GetDiskSerialNumberUsingStorageWmi()
{
    string serialNumber = string.Empty;
    try
    {
        ManagementScope scope = new ManagementScope(@"\\.\root\Microsoft\Windows\Storage");
        scope.Connect();
        
        ObjectQuery query = new ObjectQuery("SELECT SerialNumber FROM MSFT_PhysicalDisk");
        ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
        
        foreach (ManagementObject disk in searcher.Get())
        {
            var serial = disk["SerialNumber"]?.ToString();
            if (!string.IsNullOrEmpty(serial))
            {
                // 清理序列号中的空格和特殊字符
                serialNumber = serial.Trim().Replace(" ", string.Empty);
                Console.WriteLine($"磁盘序列号: {serialNumber}");
                // 如果要获取所有磁盘,可以在这里收集列表,而不是只取第一个
                break;
            }
        }
    }
    catch (ManagementException ex)
    {
        Console.WriteLine($"WMI查询错误: {ex.Message}");
    }
    return serialNumber;
}

注意:这个方法需要管理员权限才能正常运行,否则可能会返回空或者抛出权限异常。

方案二:调用Windows SetupDi API(底层实现)

如果WMI方案还是不行,可以直接调用Windows的SetupDi系列API来枚举磁盘设备并读取序列号。这种方法更接近底层,兼容性更好,但代码相对复杂一些。

C# 代码示例(简化版)

using System;
using System.Runtime.InteropServices;

public static class DiskSerialHelper
{
    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    private const uint DIGCF_PRESENT = 0x00000002;
    private const uint DIGCF_ALLCLASSES = 0x00000004;
    private const uint SPDRP_SERIALNUMBER = 0x00000011;

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        public uint cbSize;
        public Guid ClassGuid;
        public uint DevInst;
        public IntPtr Reserved;
    }

    public static string GetDiskSerialNumber()
    {
        Guid diskClassGuid = new Guid("{4d36e967-e325-11ce-bfc1-08002be10318}"); // 磁盘设备类GUID
        IntPtr deviceInfoSet = SetupDiGetClassDevs(ref diskClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES);
        
        if (deviceInfoSet == IntPtr.Zero)
        {
            Console.WriteLine($"SetupDiGetClassDevs失败,错误码: {Marshal.GetLastWin32Error()}");
            return null;
        }

        try
        {
            SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA();
            deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData);
            
            for (uint i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, ref deviceInfoData); i++)
            {
                uint regDataType;
                uint requiredSize = 0;
                
                // 先获取需要的缓冲区大小
                SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_SERIALNUMBER, out regDataType, IntPtr.Zero, 0, out requiredSize);
                
                if (requiredSize > 0)
                {
                    IntPtr buffer = Marshal.AllocHGlobal((int)requiredSize);
                    try
                    {
                        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_SERIALNUMBER, out regDataType, buffer, requiredSize, out requiredSize))
                        {
                            string serial = Marshal.PtrToStringAuto(buffer);
                            if (!string.IsNullOrEmpty(serial))
                            {
                                return serial.Trim();
                            }
                        }
                    }
                    finally
                    {
                        Marshal.FreeHGlobal(buffer);
                    }
                }
            }
        }
        finally
        {
            SetupDiDestroyDeviceInfoList(deviceInfoSet);
        }
        
        return null;
    }
}

这个方法同样需要管理员权限,而且不同磁盘设备的序列号格式可能有差异,建议根据实际情况做额外的清理处理。

为什么原来的WMI类不行?

在Windows Server 2019中,Win32_DiskDriveSerialNumber属性并非被弃用,而是依赖于磁盘控制器和驱动是否正确报告该信息。有些虚拟化环境或者较新的存储控制器可能不会通过这个类暴露序列号,而新的MSFT_PhysicalDisk类兼容性更好,能适配更多场景。

另外,一定要确保你的程序是以管理员身份运行,普通权限下WMI可能无法读取到敏感的硬件信息。

内容的提问来源于stack exchange,提问作者NTDLS

火山引擎 最新活动