.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.         /// 
  25.         /// The main entry point for the application.
  26.         /// 
  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. /// 
  2.        /// 获取硬盘唯一序列号(不是卷标号),可能需要以管理员身份运行程序
  3.        /// 
  4.        /// 
  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.         /// 
  10.         /// 型号
  11.         /// 
  12.         public string ModuleNumber;
  13.         /// 
  14.         /// 固件版本
  15.         /// 
  16.         public string Firmware;
  17.         /// 
  18.         /// 序列号
  19.         /// 
  20.         public string SerialNumber;
  21.         /// 
  22.         /// 容量,以M为单位
  23.         /// 
  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.     /// 
  120.     /// ATAPI驱动器相关
  121.     /// 
  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.         /// 
  168.         /// 获得硬盘信息
  169.         /// 
  170.         /// 硬盘序号
  171.         /// 硬盘信息
  172.         /// 
  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.         /// 
  179.         /// 
  180.         /// AtapiDevice.GetHddInfo()
  181.         /// 
  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. }

你可能感兴趣的:(.NET获取硬盘序列号的几个方法)