.NET获取硬盘序列号的几个方法

最近作软件注册,收集了很多.NET相关的获取硬盘物理序列号的方法,主要分为使用WMI方式和API方式。但这些方法均可能有问题。

1,使用WMI方式,有的机器根本取不到硬盘序列号,有的方式在Vista下面会报错。

常用的使用WMI的方式主要有下面一些方式:

  1. class HardDrive
  2.     {
  3.         private string model = null;
  4.         private string type = null;
  5.         private string serialNo = null;
  6.         public string Model
  7.         {
  8.             get {return model;}
  9.             set {model = value;}
  10.         }
  11.         public string Type
  12.         {
  13.             get {return type;}
  14.             set {type = value;}
  15.         }
  16.         public string SerialNo
  17.         {
  18.             get {return serialNo;}
  19.             set {serialNo = value;}
  20.         }
  21.     }
  22.     class TestProgram
  23.     {
  24.         /// <summary>
  25.         /// The main entry point for the application.
  26.         /// </summary>
  27.         [STAThread]
  28.         static void Main(string[] args)
  29.         {
  30.             //在Vista下面失败
  31.             ArrayList hdCollection = new ArrayList();
  32.             ManagementObjectSearcher searcher = new
  33.                 ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
  34.             foreach(ManagementObject wmi_HD in searcher.Get())
  35.             {
  36.                 HardDrive hd = new HardDrive();
  37.                 hd.Model    = wmi_HD["Model"].ToString();
  38.                 hd.Type     = wmi_HD["InterfaceType"].ToString();
  39.                 hdCollection.Add(hd);
  40.             }
  41.             searcher = new
  42.                 ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");
  43.             int i = 0;
  44.             foreach(ManagementObject wmi_HD in searcher.Get())
  45.             {
  46.                 // get the hard drive from collection
  47.                 // using index
  48.                 HardDrive hd = (HardDrive)hdCollection[i];
  49.                 // get the hardware serial no.
  50.                 if (wmi_HD["SerialNumber"] == null)
  51.                     hd.SerialNo = "None";
  52.                 else
  53.                     hd.SerialNo = wmi_HD["SerialNumber"].ToString();
  54.                 ++i;
  55.             }
  56.             // Display available hard drives
  57.             foreach(HardDrive hd in hdCollection)
  58.             {
  59.                 Console.WriteLine("Model/t/t: " + hd.Model);
  60.                 Console.WriteLine("Type/t/t: " + hd.Type);
  61.                 Console.WriteLine("Serial No./t: " + hd.SerialNo);
  62.                 Console.WriteLine();
  63.             }
  64.             // Pause application
  65.             Console.WriteLine("Press [Enter] to exit...");
  66.             Console.ReadLine();
  67.         }
  68.     }

 上面的方式先查询Win32_DiskDrive,然后再查询 Win32_PhysicalMedia,经过测试,这种方式不能保证在所有机器上均取得硬盘序列号,而且在Vista下面还会出错,程序直接抛出无法处理的异常。

另外,还可以使用另外一WMI方式,就是查询 PNPDeviceID 的 signature,代码如下:

  1. /// <summary>
  2.        /// 获取硬盘唯一序列号(不是卷标号),可能需要以管理员身份运行程序
  3.        /// </summary>
  4.        /// <returns></returns>
  5.         public static string GetHdId()
  6.         {
  7.             ManagementObjectSearcher wmiSearcher = new ManagementObjectSearcher();
  8.             /*
  9.              * PNPDeviceID   的数据是由四部分组成的:   
  10.   1、接口,通常有   IDE,ATA,SCSI;   
  11.   2、型号   
  12.   3、(可能)驱动版本号   
  13.   4、(可能)硬盘的出厂序列号   
  14.              * 
  15.              * 
  16.              */
  17.             //signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行,查相关资料说,可能在2000系统上获取的值为空)
  18.             wmiSearcher.Query = new SelectQuery(
  19.             "Win32_DiskDrive",
  20.             "",
  21.             new string[] { "PNPDeviceID""signature" }
  22.             );
  23.             ManagementObjectCollection myCollection = wmiSearcher.Get();
  24.             ManagementObjectCollection.ManagementObjectEnumerator em =
  25.             myCollection.GetEnumerator();
  26.             em.MoveNext();
  27.             ManagementBaseObject mo = em.Current;
  28.             //string id = mo.Properties["PNPDeviceID"].Value.ToString().Trim();
  29.             string id = mo.Properties["signature"].Value.ToString().Trim();
  30.             return id;
  31.         }

有人说,使用 signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行),而且查询相关资料说,可能在2000系统上获取的值为空。

使用这种方式,在Vista上面工作良好。

经过测试,使用 signature  均能够取得硬盘序列号,但是跟 Win32_PhysicalMedia 查询出来的号不一样。目前我也不能肯定 使用 signature  能够100%取道硬盘序列号。

使用WMI方式需要客户机开启WMI服务,但这个往往不能保证,所以使用这种方式有一定局限性。

2,使用API方式。

在网上找到一片资料,说使用 RING3调用 API DeviceIoControl()来获取硬盘信息,下面是原话:

硬盘序列号(Serial Number)不等于卷标号(Volume Name),后者虽然很容易得到,但是格式化分区后就会重写,不可靠。遗憾的是很多朋友往往分不清这一点。

要得到硬盘的物理序列号,可以通过WMI,也就是Win32_PhysicalMedia.SerialNumber。可惜的是Windows 98/ME的WMI并不支持这个类,访问时会出现异常。

受陆麟的例子的启发,我们还可以通过S.M.A.R.T.接口,直接从RING3调用 API DeviceIoControl()来获取硬盘信息,而不需要写VXD或者DRIVER。这样这个问题就解决了,我对它进行了封装,大量使用了 P/Invoke技术,一个完整的Library。支持Windows 98-2003。

使用上很简单:

HardDiskInfo hdd = AtapiDevice.GetHddInfo(0); // 第一个硬盘
Console.WriteLine("Module Number: {0}", hdd.ModuleNumber);
Console.WriteLine("Serial Number: {0}", hdd.SerialNumber);
Console.WriteLine("Firmware: {0}", hdd.Firmware);
Console.WriteLine("Capacity: {0} M", hdd.Capacity);

感谢原文作者的贡献,(在这里我已经不知道原文作者是谁了,网上的文章都是转载的),经过测试,这种方式比较准确,但是需要管理员权限运行。

下面把代码分享:

    1. using System;
    2. using System.Runtime.InteropServices;
    3. using System.Text;
    4. namespace HardwareUtility
    5. {
    6.     [Serializable]
    7.     public struct HardDiskInfo
    8.     {
    9.         /// <summary>
    10.         /// 型号
    11.         /// </summary>
    12.         public string ModuleNumber;
    13.         /// <summary>
    14.         /// 固件版本
    15.         /// </summary>
    16.         public string Firmware;
    17.         /// <summary>
    18.         /// 序列号
    19.         /// </summary>
    20.         public string SerialNumber;
    21.         /// <summary>
    22.         /// 容量,以M为单位
    23.         /// </summary>
    24.         public uint Capacity;
    25.     }
    26.     #region Internal Structs
    27.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    28.     internal struct GetVersionOutParams
    29.     {
    30.         public byte bVersion;
    31.         public byte bRevision;
    32.         public byte bReserved;
    33.         public byte bIDEDeviceMap;
    34.         public uint fCapabilities;
    35.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    36.         public uint[] dwReserved; // For future use.
    37.     }
    38.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    39.     internal struct IdeRegs
    40.     {
    41.         public byte bFeaturesReg;
    42.         public byte bSectorCountReg;
    43.         public byte bSectorNumberReg;
    44.         public byte bCylLowReg;
    45.         public byte bCylHighReg;
    46.         public byte bDriveHeadReg;
    47.         public byte bCommandReg;
    48.         public byte bReserved;
    49.     }
    50.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    51.     internal struct SendCmdInParams
    52.     {
    53.         public uint cBufferSize;
    54.         public IdeRegs irDriveRegs;
    55.         public byte bDriveNumber;
    56.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    57.         public byte[] bReserved;
    58.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    59.         public uint[] dwReserved;
    60.         public byte bBuffer;
    61.     }
    62.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    63.     internal struct DriverStatus
    64.     {
    65.         public byte bDriverError;
    66.         public byte bIDEStatus;
    67.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    68.         public byte[] bReserved;
    69.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    70.         public uint[] dwReserved;
    71.     }
    72.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
    73.     internal struct SendCmdOutParams
    74.     {
    75.         public uint cBufferSize;
    76.         public DriverStatus DriverStatus;
    77.         public IdSector bBuffer;
    78.     }
    79.     [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
    80.     internal struct IdSector
    81.     {
    82.         public ushort wGenConfig;
    83.         public ushort wNumCyls;
    84.         public ushort wReserved;
    85.         public ushort wNumHeads;
    86.         public ushort wBytesPerTrack;
    87.         public ushort wBytesPerSector;
    88.         public ushort wSectorsPerTrack;
    89.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    90.         public ushort[] wVendorUnique;
    91.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    92.         public byte[] sSerialNumber;
    93.         public ushort wBufferType;
    94.         public ushort wBufferSize;
    95.         public ushort wECCSize;
    96.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    97.         public byte[] sFirmwareRev;
    98.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
    99.         public byte[] sModelNumber;
    100.         public ushort wMoreVendorUnique;
    101.         public ushort wDoubleWordIO;
    102.         public ushort wCapabilities;
    103.         public ushort wReserved1;
    104.         public ushort wPIOTiming;
    105.         public ushort wDMATiming;
    106.         public ushort wBS;
    107.         public ushort wNumCurrentCyls;
    108.         public ushort wNumCurrentHeads;
    109.         public ushort wNumCurrentSectorsPerTrack;
    110.         public uint ulCurrentSectorCapacity;
    111.         public ushort wMultSectorStuff;
    112.         public uint ulTotalAddressableSectors;
    113.         public ushort wSingleWordDMA;
    114.         public ushort wMultiWordDMA;
    115.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
    116.         public byte[] bReserved;
    117.     }
    118.     #endregion
    119.     /// <summary>
    120.     /// ATAPI驱动器相关
    121.     /// </summary>
    122.     public class AtapiDevice
    123.     {
    124.         #region DllImport
    125.         [DllImport("kernel32.dll", SetLastError = true)]
    126.         static extern int CloseHandle(IntPtr hObject);
    127.         [DllImport("kernel32.dll", SetLastError = true)]
    128.         static extern IntPtr CreateFile(
    129.             string lpFileName,
    130.             uint dwDesiredAccess,
    131.             uint dwShareMode,
    132.             IntPtr lpSecurityAttributes,
    133.             uint dwCreationDisposition,
    134.             uint dwFlagsAndAttributes,
    135.             IntPtr hTemplateFile);
    136.         [DllImport("kernel32.dll")]
    137.         static extern int DeviceIoControl(
    138.             IntPtr hDevice,
    139.             uint dwIoControlCode,
    140.             IntPtr lpInBuffer,
    141.             uint nInBufferSize,
    142.             ref GetVersionOutParams lpOutBuffer,
    143.             uint nOutBufferSize,
    144.             ref uint lpBytesReturned,
    145.             [Out] IntPtr lpOverlapped);
    146.         [DllImport("kernel32.dll")]
    147.         static extern int DeviceIoControl(
    148.             IntPtr hDevice,
    149.             uint dwIoControlCode,
    150.             ref SendCmdInParams lpInBuffer,
    151.             uint nInBufferSize,
    152.             ref SendCmdOutParams lpOutBuffer,
    153.             uint nOutBufferSize,
    154.             ref uint lpBytesReturned,
    155.             [Out] IntPtr lpOverlapped);
    156.         const uint DFP_GET_VERSION = 0x00074080;
    157.         const uint DFP_SEND_DRIVE_COMMAND = 0x0007c084;
    158.         const uint DFP_RECEIVE_DRIVE_DATA = 0x0007c088;
    159.         const uint GENERIC_READ = 0x80000000;
    160.         const uint GENERIC_WRITE = 0x40000000;
    161.         const uint FILE_SHARE_READ = 0x00000001;
    162.         const uint FILE_SHARE_WRITE = 0x00000002;
    163.         const uint CREATE_NEW = 1;
    164.         const uint OPEN_EXISTING = 3;
    165.         #endregion
    166.         #region GetHddInfo
    167.         /// <summary>
    168.         /// 获得硬盘信息
    169.         /// </summary>
    170.         /// <param name="driveIndex">硬盘序号</param>
    171.         /// <returns>硬盘信息</returns>
    172.         /// <remarks>
    173.         /// 参考lu0的文章:http://lu0s1.3322.org/App/2k1103.html
    174.         /// by sunmast for everyone
    175.         /// thanks lu0 for his great works
    176.         /// 在Windows 98/ME中,S.M.A.R.T并不缺省安装,请将SMARTVSD.VXD拷贝到%SYSTEM%/IOSUBSYS目录下。
    177.         /// 在Windows 2000/2003下,需要Administrators组的权限。
    178.         /// </remarks>
    179.         /// <example>
    180.         /// AtapiDevice.GetHddInfo()
    181.         /// </example>
    182.         public static HardDiskInfo GetHddInfo(byte driveIndex)
    183.         {
    184.             switch (Environment.OSVersion.Platform)
    185.             {
    186.                 case PlatformID.Win32Windows:
    187.                     return GetHddInfo9x(driveIndex);
    188.                 case PlatformID.Win32NT:
    189.                     return GetHddInfoNT(driveIndex);
    190.                 case PlatformID.Win32S:
    191.                     throw new NotSupportedException("Win32s is not supported.");
    192.                 case PlatformID.WinCE:
    193.                     throw new NotSupportedException("WinCE is not supported.");
    194.                 default:
    195.                     throw new NotSupportedException("Unknown Platform.");
    196.             }
    197.         }
    198.         #region GetHddInfo9x
    199.         private static HardDiskInfo GetHddInfo9x(byte driveIndex)
    200.         {
    201.             GetVersionOutParams vers = new GetVersionOutParams();
    202.             SendCmdInParams inParam = new SendCmdInParams();
    203.             SendCmdOutParams outParam = new SendCmdOutParams();
    204.             uint bytesReturned = 0;
    205.             IntPtr hDevice = CreateFile(
    206.                 @"//./Smartvsd",
    207.                 0,
    208.                 0,
    209.                 IntPtr.Zero,
    210.                 CREATE_NEW,
    211.                 0,
    212.                 IntPtr.Zero);
    213.             if (hDevice == IntPtr.Zero)
    214.             {
    215.                 throw new Exception("Open smartvsd.vxd failed.");
    216.             }
    217.             if (0 == DeviceIoControl(
    218.                 hDevice,
    219.                 DFP_GET_VERSION,
    220.                 IntPtr.Zero,
    221.                 0,
    222.                 ref vers,
    223.                 (uint)Marshal.SizeOf(vers),
    224.                 ref bytesReturned,
    225.                 IntPtr.Zero))
    226.             {
    227.                 CloseHandle(hDevice);
    228.                 throw new Exception("DeviceIoControl failed:DFP_GET_VERSION");
    229.             }
    230.             // If IDE identify command not supported, fails
    231.             if (0 == (vers.fCapabilities & 1))
    232.             {
    233.                 CloseHandle(hDevice);
    234.                 throw new Exception("Error: IDE identify command not supported.");
    235.             }
    236.             if (0 != (driveIndex & 1))
    237.             {
    238.                 inParam.irDriveRegs.bDriveHeadReg = 0xb0;
    239.             }
    240.             else
    241.             {
    242.                 inParam.irDriveRegs.bDriveHeadReg = 0xa0;
    243.             }
    244.             if (0 != (vers.fCapabilities & (16 >> driveIndex)))
    245.             {
    246.                 // We don't detect a ATAPI device.
    247.                 CloseHandle(hDevice);
    248.                 throw new Exception(string.Format("Drive {0} is a ATAPI device, we don't detect it", driveIndex + 1));
    249.             }
    250.             else
    251.             {
    252.                 inParam.irDriveRegs.bCommandReg = 0xec;
    253.             }
    254.             inParam.bDriveNumber = driveIndex;
    255.             inParam.irDriveRegs.bSectorCountReg = 1;
    256.             inParam.irDriveRegs.bSectorNumberReg = 1;
    257.             inParam.cBufferSize = 512;
    258.             if (0 == DeviceIoControl(
    259.                 hDevice,
    260.                 DFP_RECEIVE_DRIVE_DATA,
    261.                 ref inParam,
    262.                 (uint)Marshal.SizeOf(inParam),
    263.                 ref outParam,
    264.                 (uint)Marshal.SizeOf(outParam),
    265.                 ref bytesReturned,
    266.                 IntPtr.Zero))
    267.             {
    268.                 CloseHandle(hDevice);
    269.                 throw new Exception("DeviceIoControl failed: DFP_RECEIVE_DRIVE_DATA");
    270.             }
    271.             CloseHandle(hDevice);
    272.             return GetHardDiskInfo(outParam.bBuffer);
    273.         }
    274.         #endregion
    275.         #region GetHddInfoNT
    276.         private static HardDiskInfo GetHddInfoNT(byte driveIndex)
    277.         {
    278.             GetVersionOutParams vers = new GetVersionOutParams();
    279.             SendCmdInParams inParam = new SendCmdInParams();
    280.             SendCmdOutParams outParam = new SendCmdOutParams();
    281.             uint bytesReturned = 0;
    282.             // We start in NT/Win2000
    283.             IntPtr hDevice = CreateFile(
    284.                 string.Format(@"//./PhysicalDrive{0}", driveIndex),
    285.                 GENERIC_READ | GENERIC_WRITE,
    286.                 FILE_SHARE_READ | FILE_SHARE_WRITE,
    287.                 IntPtr.Zero,
    288.                 OPEN_EXISTING,
    289.                 0,
    290.                 IntPtr.Zero);
    291.             if (hDevice == IntPtr.Zero)
    292.             {
    293.                 throw new Exception("CreateFile faild.");
    294.             }
    295.             if (0 == DeviceIoControl(
    296.                 hDevice,
    297.                 DFP_GET_VERSION,
    298.                 IntPtr.Zero,
    299.                 0,
    300.                 ref vers,
    301.                 (uint)Marshal.SizeOf(vers),
    302.                 ref bytesReturned,
    303.                 IntPtr.Zero))
    304.             {
    305.                 CloseHandle(hDevice);
    306.                 throw new Exception(string.Format("Drive {0} may not exists.", driveIndex + 1));
    307.             }
    308.             // If IDE identify command not supported, fails
    309.             if (0 == (vers.fCapabilities & 1))
    310.             {
    311.                 CloseHandle(hDevice);
    312.                 throw new Exception("Error: IDE identify command not supported.");
    313.             }
    314.             // Identify the IDE drives
    315.             if (0 != (driveIndex & 1))
    316.             {
    317.                 inParam.irDriveRegs.bDriveHeadReg = 0xb0;
    318.             }
    319.             else
    320.             {
    321.                 inParam.irDriveRegs.bDriveHeadReg = 0xa0;
    322.             }
    323.             if (0 != (vers.fCapabilities & (16 >> driveIndex)))
    324.             {
    325.                 // We don't detect a ATAPI device.
    326.                 CloseHandle(hDevice);
    327.                 throw new Exception(string.Format("Drive {0} is a ATAPI device, we don't detect it.", driveIndex + 1));
    328.             }
    329.             else
    330.             {
    331.                 inParam.irDriveRegs.bCommandReg = 0xec;
    332.             }
    333.             inParam.bDriveNumber = driveIndex;
    334.             inParam.irDriveRegs.bSectorCountReg = 1;
    335.             inParam.irDriveRegs.bSectorNumberReg = 1;
    336.             inParam.cBufferSize = 512;
    337.             if (0 == DeviceIoControl(
    338.                 hDevice,
    339.                 DFP_RECEIVE_DRIVE_DATA,
    340.                 ref inParam,
    341.                 (uint)Marshal.SizeOf(inParam),
    342.                 ref outParam,
    343.                 (uint)Marshal.SizeOf(outParam),
    344.                 ref bytesReturned,
    345.                 IntPtr.Zero))
    346.             {
    347.                 CloseHandle(hDevice);
    348.                 throw new Exception("DeviceIoControl failed: DFP_RECEIVE_DRIVE_DATA");
    349.             }
    350.             CloseHandle(hDevice);
    351.             return GetHardDiskInfo(outParam.bBuffer);
    352.         }
    353.         #endregion
    354.         private static HardDiskInfo GetHardDiskInfo(IdSector phdinfo)
    355.         {
    356.             HardDiskInfo hddInfo = new HardDiskInfo();
    357.             ChangeByteOrder(phdinfo.sModelNumber);
    358.             hddInfo.ModuleNumber = Encoding.ASCII.GetString(phdinfo.sModelNumber).Trim();
    359.             ChangeByteOrder(phdinfo.sFirmwareRev);
    360.             hddInfo.Firmware = Encoding.ASCII.GetString(phdinfo.sFirmwareRev).Trim();
    361.             ChangeByteOrder(phdinfo.sSerialNumber);
    362.             hddInfo.SerialNumber = Encoding.ASCII.GetString(phdinfo.sSerialNumber).Trim();
    363.             hddInfo.Capacity = phdinfo.ulTotalAddressableSectors / 2 / 1024;
    364.             return hddInfo;
    365.         }
    366.         private static void ChangeByteOrder(byte[] charArray)
    367.         {
    368.             byte temp;
    369.             for (int i = 0; i < charArray.Length; i += 2)
    370.             {
    371.                 temp = charArray[i];
    372.                 charArray[i] = charArray[i + 1];
    373.                 charArray[i + 1] = temp;
    374.             }
    375.         }
    376.         #endregion
    377.     }
    378. }
原文地址:https://www.cnblogs.com/sandy/p/3114517.html