来源不详:
最近作软件注册,收集了很多.NET相关的获取硬盘物理序列号的方法,主要分为使用WMI方式和API方式。但这些方法均可能有问题。
1,使用WMI方式,有的机器根本取不到硬盘序列号,有的方式在Vista下面会报错。
常用的使用WMI的方式主要有下面一些方式:
class HardDrive
{
privatestring model = null;
privatestring type = null;
privatestring serialNo = null;
publicstring Model
{
get{return model;}
set{model = value;}
}
publicstring Type
{
get{return type;}
set{type = value;}
}
publicstring SerialNo
{
get{return serialNo;}
set{serialNo = value;}
}
}
classTestProgram
{
///
/// Themain entry point for the application.
///
[STAThread]
staticvoid 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");
inti = 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,代码如下:
///
/// 获取硬盘唯一序列号(不是卷标号),可能需要以管理员身份运行程序
///
///
publicstatic string GetHdId()
{
ManagementObjectSearcher wmiSearcher = new ManagementObjectSearcher();
/*
*PNPDeviceID 的数据是由四部分组成的:
1、接口,通常有 IDE,ATA,SCSI;
2、型号
3、(可能)驱动版本号
4、(可能)硬盘的出厂序列号
*
*
*/
//signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行,查相关资料说,可能在2000系统上获取的值为空)
wmiSearcher.Query = new SelectQuery(
"Win32_DiskDrive",
"",
newstring[] { "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调用 APIDeviceIoControl()来获取硬盘信息,下面是原话:
硬盘序列号(Serial Number)不等于卷标号(VolumeName),后者虽然很容易得到,但是格式化分区后就会重写,不可靠。遗憾的是很多朋友往往分不清这一点。
要得到硬盘的物理序列号,可以通过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]
publicstruct HardDiskInfo
{
///
/// 型号
///
publicstring ModuleNumber;
///
/// 固件版本
///
publicstring Firmware;
///
/// 序列号
///
publicstring SerialNumber;
///
/// 容量,以M为单位
///
publicuint Capacity;
}
#regionInternal Structs
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internalstruct GetVersionOutParams
{
publicbyte bVersion;
public byte bRevision;
publicbyte bReserved;
publicbyte bIDEDeviceMap;
publicuint fCapabilities;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
publicuint[] dwReserved; // For future use.
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internalstruct IdeRegs
{
publicbyte bFeaturesReg;
publicbyte bSectorCountReg;
publicbyte bSectorNumberReg;
publicbyte bCylLowReg;
publicbyte bCylHighReg;
publicbyte bDriveHeadReg;
publicbyte bCommandReg;
publicbyte bReserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internalstruct SendCmdInParams
{
publicuint cBufferSize;
public IdeRegs irDriveRegs;
publicbyte bDriveNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
publicbyte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
publicuint[] dwReserved;
publicbyte bBuffer;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internalstruct DriverStatus
{
publicbyte bDriverError;
publicbyte bIDEStatus;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
publicbyte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
publicuint[] dwReserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct SendCmdOutParams
{
publicuint cBufferSize;
publicDriverStatus DriverStatus;
publicIdSector bBuffer;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
internalstruct IdSector
{
publicushort wGenConfig;
publicushort wNumCyls;
publicushort wReserved;
publicushort wNumHeads;
publicushort wBytesPerTrack;
publicushort wBytesPerSector;
publicushort wSectorsPerTrack;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
publicushort[] wVendorUnique;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
publicbyte[] sSerialNumber;
publicushort wBufferType;
publicushort wBufferSize;
publicushort wECCSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
publicbyte[] sFirmwareRev;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
publicbyte[] sModelNumber;
publicushort wMoreVendorUnique;
publicushort wDoubleWordIO;
publicushort wCapabilities;
publicushort wReserved1;
publicushort wPIOTiming;
publicushort wDMATiming;
publicushort wBS;
publicushort wNumCurrentCyls;
publicushort wNumCurrentHeads;
publicushort wNumCurrentSectorsPerTrack;
publicuint ulCurrentSectorCapacity;
publicushort wMultSectorStuff;
publicuint ulTotalAddressableSectors;
publicushort wSingleWordDMA;
publicushort wMultiWordDMA;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
publicbyte[] bReserved;
}
#endregion
///
/// ATAPI驱动器相关
///
public classAtapiDevice
{
#regionDllImport
[DllImport("kernel32.dll", SetLastError = true)]
staticextern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
staticextern IntPtr CreateFile(
string lpFileName,
uintdwDesiredAccess,
uintdwShareMode,
IntPtr lpSecurityAttributes,
uintdwCreationDisposition,
uintdwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll")]
staticextern int DeviceIoControl(
IntPtr hDevice,
uintdwIoControlCode,
IntPtr lpInBuffer,
uintnInBufferSize,
refGetVersionOutParams lpOutBuffer,
uintnOutBufferSize,
refuint lpBytesReturned,
[Out] IntPtr lpOverlapped);
[DllImport("kernel32.dll")]
staticextern int DeviceIoControl(
IntPtr hDevice,
uintdwIoControlCode,
refSendCmdInParams lpInBuffer,
uintnInBufferSize,
refSendCmdOutParams lpOutBuffer,
uintnOutBufferSize,
refuint lpBytesReturned,
[Out] IntPtr lpOverlapped);
constuint DFP_GET_VERSION = 0x00074080;
constuint DFP_SEND_DRIVE_COMMAND = 0x0007c084;
constuint DFP_RECEIVE_DRIVE_DATA = 0x0007c088;
constuint GENERIC_READ = 0x80000000;
constuint GENERIC_WRITE = 0x40000000;
constuint FILE_SHARE_READ = 0x00000001;
constuint FILE_SHARE_WRITE = 0x00000002;
constuint CREATE_NEW = 1;
constuint OPEN_EXISTING = 3;
#endregion
#region GetHddInfo
///
/// 获得硬盘信息
///
///硬盘序号
///
///
/// 参考lu0的文章:http://lu0s1.3322.org/App/2k1103.html
/// bysunmast for everyone
///thanks lu0 for his great works
/// 在Windows98/ME中,S.M.A.R.T并不缺省安装,请将SMARTVSD.VXD拷贝到%SYSTEM%\IOSUBSYS目录下。
/// 在Windows2000/2003下,需要Administrators组的权限。
///
///
///AtapiDevice.GetHddInfo()
///
publicstatic 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.");
}
}
#regionGetHddInfo9x
privatestatic HardDiskInfo GetHddInfo9x(byte driveIndex)
{
GetVersionOutParams vers = new GetVersionOutParams();
SendCmdInParams inParam = new SendCmdInParams();
SendCmdOutParams outParam = new SendCmdOutParams();
uintbytesReturned = 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 notsupported.");
}
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, wedon'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
#regionGetHddInfoNT
privatestatic HardDiskInfo GetHddInfoNT(byte driveIndex)
{
GetVersionOutParams vers = new GetVersionOutParams();
SendCmdInParams inParam = new SendCmdInParams();
SendCmdOutParams outParam = new SendCmdOutParams();
uintbytesReturned = 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 notsupported.");
}
//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, wedon'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
privatestatic 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;
}
privatestatic void ChangeByteOrder(byte[] charArray)
{
bytetemp;
for(int i = 0; i < charArray.Length; i += 2)
{
temp = charArray[i];
charArray[i] = charArray[i + 1];
charArray[i + 1] = temp;
}
}
#endregion
}
}