|   硬盘序列号(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 Sunmast.Hardware{
 [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/Win2000IntPtr 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}
 }
   注:   在Windows 98/ME中,S.M.A.R.T并不缺省安装,请将SMARTVSD.VXD拷贝到%SYSTEM%\IOSUBSYS目录下。在Windows 2000/2003下,需要Administrators组的权限。
 不要在装有SCSI硬盘的机器上尝试了,因为SCSI硬盘根本不存在序列号。
   最终版权归陆麟所有,任何人不得将此代码占为己有。 |