zoukankan      html  css  js  c++  java
  • 如何得到硬盘序列号[C#]


    硬盘序列号(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/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
    }
    }



    注:


    在Windows 98/ME中,S.M.A.R.T并不缺省安装,请将SMARTVSD.VXD拷贝到%SYSTEM%\IOSUBSYS目录下。
    在Windows 2000/2003下,需要Administrators组的权限。
    不要在装有SCSI硬盘的机器上尝试了,因为SCSI硬盘根本不存在序列号。


    最终版权归陆麟所有,任何人不得将此代码占为己有。

  • 相关阅读:
    LINUX监控一:监控命令
    Kettle并行
    KETTLE集群搭建
    Solr报错Index locked for write for core '***'. Solr now longer supports forceful unlocking via 'unlockOnStartup'
    Solr json,xml等文件数据导入(添加索引)linux下操作
    python对solr进行查询、插入操作(GETPOST)
    Solr-5.3.1 dataimport 导入mysql数据
    解决MySQL数据导入报错Got a packet bigger than‘max_allowed_packet’bytes
    解决防火墙限制远程连接MySQL(导致错误10060可能之一)
    gensim加载word2vec训练结果(bin文件)并进行相似度实验
  • 原文地址:https://www.cnblogs.com/draeag/p/791992.html
Copyright © 2011-2022 走看看