最近作软件注册,收集了很多.NET相关的获取硬盘物理序列号的方法,主要分为使用WMI方式和API方式。但这些方法均可能有问题。
1,使用WMI方式,有的机器根本取不到硬盘序列号,有的方式在Vista下面会报错。
常用的使用WMI的方式主要有下面一些方式:
- class HardDrive
- {
- private string model = null;
- private string type = null;
- private string serialNo = null;
- public string Model
- {
- get {return model;}
- set {model = value;}
- }
- public string Type
- {
- get {return type;}
- set {type = value;}
- }
- public string SerialNo
- {
- get {return serialNo;}
- set {serialNo = value;}
- }
- }
- class TestProgram
- {
- /// <summary>
- /// The main entry point for the application.
- /// </summary>
- [STAThread]
- static void Main(string[] args)
- {
- //在Vista下面失败
- ArrayList hdCollection = new ArrayList();
- ManagementObjectSearcher searcher = new
- ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
- foreach(ManagementObject wmi_HD in searcher.Get())
- {
- HardDrive hd = new HardDrive();
- hd.Model = wmi_HD["Model"].ToString();
- hd.Type = wmi_HD["InterfaceType"].ToString();
- hdCollection.Add(hd);
- }
- searcher = new
- ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");
- int i = 0;
- foreach(ManagementObject wmi_HD in searcher.Get())
- {
- // get the hard drive from collection
- // using index
- HardDrive hd = (HardDrive)hdCollection[i];
- // get the hardware serial no.
- if (wmi_HD["SerialNumber"] == null)
- hd.SerialNo = "None";
- else
- hd.SerialNo = wmi_HD["SerialNumber"].ToString();
- ++i;
- }
- // Display available hard drives
- foreach(HardDrive hd in hdCollection)
- {
- Console.WriteLine("Model/t/t: " + hd.Model);
- Console.WriteLine("Type/t/t: " + hd.Type);
- Console.WriteLine("Serial No./t: " + hd.SerialNo);
- Console.WriteLine();
- }
- // Pause application
- Console.WriteLine("Press [Enter] to exit...");
- Console.ReadLine();
- }
- }
上面的方式先查询Win32_DiskDrive,然后再查询 Win32_PhysicalMedia,经过测试,这种方式不能保证在所有机器上均取得硬盘序列号,而且在Vista下面还会出错,程序直接抛出无法处理的异常。
另外,还可以使用另外一WMI方式,就是查询 PNPDeviceID 的 signature,代码如下:
- /// <summary>
- /// 获取硬盘唯一序列号(不是卷标号),可能需要以管理员身份运行程序
- /// </summary>
- /// <returns></returns>
- public static string GetHdId()
- {
- ManagementObjectSearcher wmiSearcher = new ManagementObjectSearcher();
- /*
- * PNPDeviceID 的数据是由四部分组成的:
- 1、接口,通常有 IDE,ATA,SCSI;
- 2、型号
- 3、(可能)驱动版本号
- 4、(可能)硬盘的出厂序列号
- *
- *
- */
- //signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行,查相关资料说,可能在2000系统上获取的值为空)
- wmiSearcher.Query = new SelectQuery(
- "Win32_DiskDrive",
- "",
- new string[] { "PNPDeviceID", "signature" }
- );
- ManagementObjectCollection myCollection = wmiSearcher.Get();
- ManagementObjectCollection.ManagementObjectEnumerator em =
- myCollection.GetEnumerator();
- em.MoveNext();
- ManagementBaseObject mo = em.Current;
- //string id = mo.Properties["PNPDeviceID"].Value.ToString().Trim();
- string id = mo.Properties["signature"].Value.ToString().Trim();
- return id;
- }
有人说,使用 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);
感谢原文作者的贡献,(在这里我已经不知道原文作者是谁了,网上的文章都是转载的),经过测试,这种方式比较准确,但是需要管理员权限运行。
下面把代码分享:
- using System;
- using System.Runtime.InteropServices;
- using System.Text;
- namespace HardwareUtility
- {
- [Serializable]
- public struct HardDiskInfo
- {
- /// <summary>
- /// 型号
- /// </summary>
- public string ModuleNumber;
- /// <summary>
- /// 固件版本
- /// </summary>
- public string Firmware;
- /// <summary>
- /// 序列号
- /// </summary>
- public string SerialNumber;
- /// <summary>
- /// 容量,以M为单位
- /// </summary>
- public uint Capacity;
- }
- #region Internal Structs
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- internal struct GetVersionOutParams
- {
- public byte bVersion;
- public byte bRevision;
- public byte bReserved;
- public byte bIDEDeviceMap;
- public uint fCapabilities;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- public uint[] dwReserved; // For future use.
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- internal struct IdeRegs
- {
- public byte bFeaturesReg;
- public byte bSectorCountReg;
- public byte bSectorNumberReg;
- public byte bCylLowReg;
- public byte bCylHighReg;
- public byte bDriveHeadReg;
- public byte bCommandReg;
- public byte bReserved;
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- internal struct SendCmdInParams
- {
- public uint cBufferSize;
- public IdeRegs irDriveRegs;
- public byte bDriveNumber;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
- public byte[] bReserved;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- public uint[] dwReserved;
- public byte bBuffer;
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- internal struct DriverStatus
- {
- public byte bDriverError;
- public byte bIDEStatus;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
- public byte[] bReserved;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
- public uint[] dwReserved;
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- internal struct SendCmdOutParams
- {
- public uint cBufferSize;
- public DriverStatus DriverStatus;
- public IdSector bBuffer;
- }
- [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
- internal struct IdSector
- {
- public ushort wGenConfig;
- public ushort wNumCyls;
- public ushort wReserved;
- public ushort wNumHeads;
- public ushort wBytesPerTrack;
- public ushort wBytesPerSector;
- public ushort wSectorsPerTrack;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
- public ushort[] wVendorUnique;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
- public byte[] sSerialNumber;
- public ushort wBufferType;
- public ushort wBufferSize;
- public ushort wECCSize;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
- public byte[] sFirmwareRev;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
- public byte[] sModelNumber;
- public ushort wMoreVendorUnique;
- public ushort wDoubleWordIO;
- public ushort wCapabilities;
- public ushort wReserved1;
- public ushort wPIOTiming;
- public ushort wDMATiming;
- public ushort wBS;
- public ushort wNumCurrentCyls;
- public ushort wNumCurrentHeads;
- public ushort wNumCurrentSectorsPerTrack;
- public uint ulCurrentSectorCapacity;
- public ushort wMultSectorStuff;
- public uint ulTotalAddressableSectors;
- public ushort wSingleWordDMA;
- public ushort wMultiWordDMA;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
- public byte[] bReserved;
- }
- #endregion
- /// <summary>
- /// ATAPI驱动器相关
- /// </summary>
- public class AtapiDevice
- {
- #region DllImport
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern int CloseHandle(IntPtr hObject);
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern IntPtr CreateFile(
- string lpFileName,
- uint dwDesiredAccess,
- uint dwShareMode,
- IntPtr lpSecurityAttributes,
- uint dwCreationDisposition,
- uint dwFlagsAndAttributes,
- IntPtr hTemplateFile);
- [DllImport("kernel32.dll")]
- static extern int DeviceIoControl(
- IntPtr hDevice,
- uint dwIoControlCode,
- IntPtr lpInBuffer,
- uint nInBufferSize,
- ref GetVersionOutParams lpOutBuffer,
- uint nOutBufferSize,
- ref uint lpBytesReturned,
- [Out] IntPtr lpOverlapped);
- [DllImport("kernel32.dll")]
- static extern int DeviceIoControl(
- IntPtr hDevice,
- uint dwIoControlCode,
- ref SendCmdInParams lpInBuffer,
- uint nInBufferSize,
- ref SendCmdOutParams lpOutBuffer,
- uint nOutBufferSize,
- ref uint lpBytesReturned,
- [Out] IntPtr lpOverlapped);
- const uint DFP_GET_VERSION = 0x00074080;
- const uint DFP_SEND_DRIVE_COMMAND = 0x0007c084;
- const uint DFP_RECEIVE_DRIVE_DATA = 0x0007c088;
- const uint GENERIC_READ = 0x80000000;
- const uint GENERIC_WRITE = 0x40000000;
- const uint FILE_SHARE_READ = 0x00000001;
- const uint FILE_SHARE_WRITE = 0x00000002;
- const uint CREATE_NEW = 1;
- const uint OPEN_EXISTING = 3;
- #endregion
- #region GetHddInfo
- /// <summary>
- /// 获得硬盘信息
- /// </summary>
- /// <param name="driveIndex">硬盘序号</param>
- /// <returns>硬盘信息</returns>
- /// <remarks>
- /// 参考lu0的文章:http://lu0s1.3322.org/App/2k1103.html
- /// by sunmast for everyone
- /// thanks lu0 for his great works
- /// 在Windows 98/ME中,S.M.A.R.T并不缺省安装,请将SMARTVSD.VXD拷贝到%SYSTEM%/IOSUBSYS目录下。
- /// 在Windows 2000/2003下,需要Administrators组的权限。
- /// </remarks>
- /// <example>
- /// AtapiDevice.GetHddInfo()
- /// </example>
- public static HardDiskInfo GetHddInfo(byte driveIndex)
- {
- switch (Environment.OSVersion.Platform)
- {
- case PlatformID.Win32Windows:
- return GetHddInfo9x(driveIndex);
- case PlatformID.Win32NT:
- return GetHddInfoNT(driveIndex);
- case PlatformID.Win32S:
- throw new NotSupportedException("Win32s is not supported.");
- case PlatformID.WinCE:
- throw new NotSupportedException("WinCE is not supported.");
- default:
- throw new NotSupportedException("Unknown Platform.");
- }
- }
- #region GetHddInfo9x
- private static HardDiskInfo GetHddInfo9x(byte driveIndex)
- {
- GetVersionOutParams vers = new GetVersionOutParams();
- SendCmdInParams inParam = new SendCmdInParams();
- SendCmdOutParams outParam = new SendCmdOutParams();
- uint bytesReturned = 0;
- IntPtr hDevice = CreateFile(
- @"//./Smartvsd",
- 0,
- 0,
- IntPtr.Zero,
- CREATE_NEW,
- 0,
- IntPtr.Zero);
- if (hDevice == IntPtr.Zero)
- {
- throw new Exception("Open smartvsd.vxd failed.");
- }
- if (0 == DeviceIoControl(
- hDevice,
- DFP_GET_VERSION,
- IntPtr.Zero,
- 0,
- ref vers,
- (uint)Marshal.SizeOf(vers),
- ref bytesReturned,
- IntPtr.Zero))
- {
- CloseHandle(hDevice);
- throw new Exception("DeviceIoControl failed:DFP_GET_VERSION");
- }
- // If IDE identify command not supported, fails
- if (0 == (vers.fCapabilities & 1))
- {
- CloseHandle(hDevice);
- throw new Exception("Error: IDE identify command not supported.");
- }
- if (0 != (driveIndex & 1))
- {
- inParam.irDriveRegs.bDriveHeadReg = 0xb0;
- }
- else
- {
- inParam.irDriveRegs.bDriveHeadReg = 0xa0;
- }
- if (0 != (vers.fCapabilities & (16 >> driveIndex)))
- {
- // We don't detect a ATAPI device.
- CloseHandle(hDevice);
- throw new Exception(string.Format("Drive {0} is a ATAPI device, we don't detect it", driveIndex + 1));
- }
- else
- {
- inParam.irDriveRegs.bCommandReg = 0xec;
- }
- inParam.bDriveNumber = driveIndex;
- inParam.irDriveRegs.bSectorCountReg = 1;
- inParam.irDriveRegs.bSectorNumberReg = 1;
- inParam.cBufferSize = 512;
- if (0 == DeviceIoControl(
- hDevice,
- DFP_RECEIVE_DRIVE_DATA,
- ref inParam,
- (uint)Marshal.SizeOf(inParam),
- ref outParam,
- (uint)Marshal.SizeOf(outParam),
- ref bytesReturned,
- IntPtr.Zero))
- {
- CloseHandle(hDevice);
- throw new Exception("DeviceIoControl failed: DFP_RECEIVE_DRIVE_DATA");
- }
- CloseHandle(hDevice);
- return GetHardDiskInfo(outParam.bBuffer);
- }
- #endregion
- #region GetHddInfoNT
- private static HardDiskInfo GetHddInfoNT(byte driveIndex)
- {
- GetVersionOutParams vers = new GetVersionOutParams();
- SendCmdInParams inParam = new SendCmdInParams();
- SendCmdOutParams outParam = new SendCmdOutParams();
- uint bytesReturned = 0;
- // We start in NT/Win2000
- IntPtr hDevice = CreateFile(
- string.Format(@"//./PhysicalDrive{0}", driveIndex),
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- IntPtr.Zero,
- OPEN_EXISTING,
- 0,
- IntPtr.Zero);
- if (hDevice == IntPtr.Zero)
- {
- throw new Exception("CreateFile faild.");
- }
- if (0 == DeviceIoControl(
- hDevice,
- DFP_GET_VERSION,
- IntPtr.Zero,
- 0,
- ref vers,
- (uint)Marshal.SizeOf(vers),
- ref bytesReturned,
- IntPtr.Zero))
- {
- CloseHandle(hDevice);
- throw new Exception(string.Format("Drive {0} may not exists.", driveIndex + 1));
- }
- // If IDE identify command not supported, fails
- if (0 == (vers.fCapabilities & 1))
- {
- CloseHandle(hDevice);
- throw new Exception("Error: IDE identify command not supported.");
- }
- // Identify the IDE drives
- if (0 != (driveIndex & 1))
- {
- inParam.irDriveRegs.bDriveHeadReg = 0xb0;
- }
- else
- {
- inParam.irDriveRegs.bDriveHeadReg = 0xa0;
- }
- if (0 != (vers.fCapabilities & (16 >> driveIndex)))
- {
- // We don't detect a ATAPI device.
- CloseHandle(hDevice);
- throw new Exception(string.Format("Drive {0} is a ATAPI device, we don't detect it.", driveIndex + 1));
- }
- else
- {
- inParam.irDriveRegs.bCommandReg = 0xec;
- }
- inParam.bDriveNumber = driveIndex;
- inParam.irDriveRegs.bSectorCountReg = 1;
- inParam.irDriveRegs.bSectorNumberReg = 1;
- inParam.cBufferSize = 512;
- if (0 == DeviceIoControl(
- hDevice,
- DFP_RECEIVE_DRIVE_DATA,
- ref inParam,
- (uint)Marshal.SizeOf(inParam),
- ref outParam,
- (uint)Marshal.SizeOf(outParam),
- ref bytesReturned,
- IntPtr.Zero))
- {
- CloseHandle(hDevice);
- throw new Exception("DeviceIoControl failed: DFP_RECEIVE_DRIVE_DATA");
- }
- CloseHandle(hDevice);
- return GetHardDiskInfo(outParam.bBuffer);
- }
- #endregion
- private static HardDiskInfo GetHardDiskInfo(IdSector phdinfo)
- {
- HardDiskInfo hddInfo = new HardDiskInfo();
- ChangeByteOrder(phdinfo.sModelNumber);
- hddInfo.ModuleNumber = Encoding.ASCII.GetString(phdinfo.sModelNumber).Trim();
- ChangeByteOrder(phdinfo.sFirmwareRev);
- hddInfo.Firmware = Encoding.ASCII.GetString(phdinfo.sFirmwareRev).Trim();
- ChangeByteOrder(phdinfo.sSerialNumber);
- hddInfo.SerialNumber = Encoding.ASCII.GetString(phdinfo.sSerialNumber).Trim();
- hddInfo.Capacity = phdinfo.ulTotalAddressableSectors / 2 / 1024;
- return hddInfo;
- }
- private static void ChangeByteOrder(byte[] charArray)
- {
- byte temp;
- for (int i = 0; i < charArray.Length; i += 2)
- {
- temp = charArray[i];
- charArray[i] = charArray[i + 1];
- charArray[i + 1] = temp;
- }
- }
- #endregion
- }
- }