2024年1月31日发(作者:)

最近作软件注册,收集了很多.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

{

///

/// The main entry point for the application.

///

[STAThread]

static void Main(string[] args)

{

//在Vista下面失败

ArrayList hdCollection = new ArrayList();

ManagementObjectSearcher searcher = new

ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");

foreach(ManagementObject wmi_HD in ())

{

HardDrive hd = new HardDrive();

= wmi_HD["Model"].ToString();

= wmi_HD["InterfaceType"].ToString();

(hd);

}

searcher = new

ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");

int i = 0;

foreach(ManagementObject wmi_HD in ())

{

// get the hard drive from collection

// using index

HardDrive hd = (HardDrive)hdCollection[i];

// get the hardware serial no.

if (wmi_HD["SerialNumber"] == null)

No = "None";

else

No = wmi_HD["SerialNumber"].ToString();

++i;

}

// Display available hard drives

foreach(HardDrive hd in hdCollection)

{

ine("Modeltt: " + );

ine("Typett: " + );

ine("Serial No.t: " + No);

ine();

}

// Pause application

ine("Press [Enter] ");

ne();

}

}

上面的方式先查询Win32_DiskDrive,然后再查询 Win32_PhysicalMedia,经过测试,这种方式不能保证在所有机器上均取得硬盘序列号,而且在Vista下面还会出错,程序直接抛出无法处理的异常。

另外,还可以使用另外一WMI方式,就是查询 PNPDeviceID 的 signature,代码如下:

///

/// 获取硬盘唯一序列号(不是卷标号),可能需要以管理员身份运行程序

///

///

public static string GetHdId()

{

ManagementObjectSearcher wmiSearcher = new ManagementObjectSearcher();

/*

* PNPDeviceID 的数据是由四部分组成的:

1、接口,通常有 IDE,ATA,SCSI;

2、型号

3、(可能)驱动版本号

4、(可能)硬盘的出厂序列号

*

*

*/

//signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行,查相关资料说,可能在2000系统上获取的值为空)

= new SelectQuery(

"Win32_DiskDrive",

"",

new string[] { "PNPDeviceID", "signature" }

);

ManagementObjectCollection myCollection = ();

mentObjectEnumerator em =

merator();

xt();

ManagementBaseObject mo = t;

//string id = ties["PNPDeviceID"].ng().Trim();

string id = ties["signature"].ng().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_Number。可惜的是Windows 98/ME的WMI并不支持这个类,访问时会出现异常。

受陆麟的例子的启发,我们还可以通过S.M.A.R.T.接口,直接从RING3调用 API

DeviceIoControl()来获取硬盘信息,而不需要写VXD或者DRIVER。这样这个问题就解决了,我对它进行了封装,大量使用了 P/Invoke技术,一个完整的Library。支持Windows 98-2003。

使用上很简单:

HardDiskInfo hdd = Info(0); // 第一个硬盘

ine("Module Number: {0}", Number);

ine("Serial Number: {0}", Number);

ine("Firmware: {0}", re);

ine("Capacity: {0} M", ty);

感谢原文作者的贡献,(在这里我已经不知道原文作者是谁了,网上的文章都是转载的),经过测试,这种方式比较准确,但是需要管理员权限运行。

下面把代码分享:

using System;

using pServices;

using ;

namespace HardwareUtility

{

[Serializable]

public struct HardDiskInfo

{

///

/// 型号

///

public string ModuleNumber;

///

/// 固件版本

///

public string Firmware;

///

/// 序列号

///

public string SerialNumber;

///

/// 容量,以M为单位

///

public uint Capacity;

}

#region Internal Structs

[StructLayout(tial, Pack = 1)]

internal struct GetVersionOutParams

{

public byte bVersion;

public byte bRevision;

public byte bReserved;

public byte bIDEDeviceMap;

public uint fCapabilities;

[MarshalAs(rray, SizeConst = 4)]

public uint[] dwReserved; // For future use.

}

[StructLayout(tial, 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(tial, Pack = 1)]

internal struct SendCmdInParams

{

public uint cBufferSize;

public IdeRegs irDriveRegs;

public byte bDriveNumber;

[MarshalAs(rray, SizeConst = 3)]

public byte[] bReserved;

[MarshalAs(rray, SizeConst = 4)]

public uint[] dwReserved;

public byte bBuffer;

}

[StructLayout(tial, Pack = 1)]

internal struct DriverStatus

{

public byte bDriverError;

public byte bIDEStatus;

[MarshalAs(rray, SizeConst = 2)]

public byte[] bReserved;

[MarshalAs(rray, SizeConst = 2)]

public uint[] dwReserved;

}

[StructLayout(tial, Pack = 1)]

internal struct SendCmdOutParams

{

public uint cBufferSize;

public DriverStatus DriverStatus;

public IdSector bBuffer;

}

[StructLayout(tial, 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(rray, SizeConst = 3)]

public ushort[] wVendorUnique;

[MarshalAs(rray, SizeConst = 20)]

public byte[] sSerialNumber;

public ushort wBufferType;

public ushort wBufferSize;

public ushort wECCSize;

[MarshalAs(rray, SizeConst = 8)]

public byte[] sFirmwareRev;

[MarshalAs(rray, 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(rray, SizeConst = 128)]

public byte[] bReserved;

}

#endregion

///

/// ATAPI驱动器相关

///

public class AtapiDevice

{

#region DllImport

[DllImport("", SetLastError = true)]

static extern int CloseHandle(IntPtr hObject);

[DllImport("", SetLastError = true)]

static extern IntPtr CreateFile(

string lpFileName,

uint dwDesiredAccess,

uint dwShareMode,

IntPtr lpSecurityAttributes,

uint dwCreationDisposition,

uint dwFlagsAndAttributes,

IntPtr hTemplateFile);

[DllImport("")]

static extern int DeviceIoControl(

IntPtr hDevice,

uint dwIoControlCode,

IntPtr lpInBuffer,

uint nInBufferSize,

ref GetVersionOutParams lpOutBuffer,

uint nOutBufferSize,

ref uint lpBytesReturned,

[Out] IntPtr lpOverlapped);

[DllImport("")]

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

///

/// 获得硬盘信息

///

/// 硬盘序号

/// 硬盘信息

///

/// 参考lu0的文章:/App/

/// by sunmast for everyone

/// thanks lu0 for his great works

/// 在Windows 98/ME中,S.M.A.R.T并不缺省安装,请将拷贝到%SYSTEM%IOSUBSYS目录下。

/// 在Windows 2000/2003下,需要Administrators组的权限。

///

///

/// Info()

///

public static HardDiskInfo GetHddInfo(byte driveIndex)

{

switch (rm)

{

case 32Windows:

return GetHddInfo9x(driveIndex);

case 32NT:

return GetHddInfoNT(driveIndex);

case 32S:

throw new NotSupportedException("Win32s is not supported.");

case :

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,

,

CREATE_NEW,

0,

);

if (hDevice == )

{

throw new Exception("Open failed.");

}

if (0 == DeviceIoControl(

hDevice,

DFP_GET_VERSION,

,

0,

ref vers,

(uint)(vers),

ref bytesReturned,

))

{

CloseHandle(hDevice);

throw new Exception("DeviceIoControl failed:DFP_GET_VERSION");

}

// If IDE identify command not supported, fails

if (0 == (ilities & 1))

{

CloseHandle(hDevice);

throw new Exception("Error: IDE identify command not supported.");

}

if (0 != (driveIndex & 1))

{

HeadReg = 0xb0;

}

else

{

HeadReg = 0xa0;

}

if (0 != (ilities & (16 >> driveIndex)))

{

// We don't detect a ATAPI device.

CloseHandle(hDevice);

throw new Exception(("Drive {0} is a ATAPI device, we

don't detect it", driveIndex + 1));

}

else

{

ndReg = 0xec;

}

Number = driveIndex;

rCountReg = 1;

rNumberReg = 1;

rSize = 512;

if (0 == DeviceIoControl(

hDevice,

DFP_RECEIVE_DRIVE_DATA,

ref inParam,

(uint)(inParam),

ref outParam,

(uint)(outParam),

ref bytesReturned,

))

{

CloseHandle(hDevice);

throw new Exception("DeviceIoControl

DFP_RECEIVE_DRIVE_DATA");

}

CloseHandle(hDevice);

return GetHardDiskInfo(r);

}

#endregion

#region GetHddInfoNT

private static HardDiskInfo GetHddInfoNT(byte driveIndex)

{

failed:

GetVersionOutParams vers = new GetVersionOutParams();

SendCmdInParams inParam = new SendCmdInParams();

SendCmdOutParams outParam = new SendCmdOutParams();

uint bytesReturned = 0;

// We start in NT/Win2000

IntPtr hDevice = CreateFile(

(@".PhysicalDrive{0}", driveIndex),

GENERIC_READ | GENERIC_WRITE,

FILE_SHARE_READ | FILE_SHARE_WRITE,

,

OPEN_EXISTING,

0,

);

if (hDevice == )

{

throw new Exception("CreateFile faild.");

}

if (0 == DeviceIoControl(

hDevice,

DFP_GET_VERSION,

,

0,

ref vers,

(uint)(vers),

ref bytesReturned,

))

{

CloseHandle(hDevice);

throw new Exception(("Drive {0} may not exists.",

driveIndex + 1));

}

// If IDE identify command not supported, fails

if (0 == (ilities & 1))

{

CloseHandle(hDevice);

throw new Exception("Error: IDE identify command not supported.");

}

// Identify the IDE drives

if (0 != (driveIndex & 1))

{

HeadReg = 0xb0;

}

else

{

HeadReg = 0xa0;

}

if (0 != (ilities & (16 >> driveIndex)))

{

// We don't detect a ATAPI device.

CloseHandle(hDevice);

throw new Exception(("Drive {0} is a ATAPI device, we

don't detect it.", driveIndex + 1));

}

else

{

ndReg = 0xec;

}

Number = driveIndex;

rCountReg = 1;

rNumberReg = 1;

rSize = 512;

if (0 == DeviceIoControl(

hDevice,

DFP_RECEIVE_DRIVE_DATA,

ref inParam,

(uint)(inParam),

ref outParam,

(uint)(outParam),

ref bytesReturned,

))

{

CloseHandle(hDevice);

throw new Exception("DeviceIoControl

DFP_RECEIVE_DRIVE_DATA");

}

CloseHandle(hDevice);

return GetHardDiskInfo(r);

}

#endregion

private static HardDiskInfo GetHardDiskInfo(IdSector phdinfo)

{

HardDiskInfo hddInfo = new HardDiskInfo();

failed:

ChangeByteOrder(Number);

Number

ing(Number).Trim();

ChangeByteOrder(areRev);

re

ing(areRev).Trim();

ChangeByteOrder(lNumber);

Number

ing(lNumber).Trim();

ty = lAddressableSectors / 2 / 1024;

return hddInfo;

}

private static void ChangeByteOrder(byte[] charArray)

{

byte temp;

for (int i = 0; i < ; i += 2)

{

temp = charArray[i];

charArray[i] = charArray[i + 1];

charArray[i + 1] = temp;

}

}

#endregion

}

}

=

=

=