• .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. }
  • 相关阅读:
    c# 把对象加入队列,对象为全局变量,对象改变队列值也跟着改变
    C# 一个数组未赋值引发的错误
    c# 2016QQ自动登录程序
    当时钟事件声明为过程变量 让system.threading.timer时钟失效
    if 循环的深入理解 哈希表的一种应用
    VB6对象与地址相互转换
    VB6的函数指针传递
    .net framework 4.0 从 GAC 卸载 程序集
    .net framework 4.0 从 GAC 卸载 程序集
    GAC in CLR 3.0
  • 原文地址:https://www.cnblogs.com/sandy/p/3114517.html
Copyright © 2020-2023  润新知