硬盤序列號(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;
IntPt