如何在 C#(无 WMI)中获取硬盘序列号?
Posted
技术标签:
【中文标题】如何在 C#(无 WMI)中获取硬盘序列号?【英文标题】:How to get Hard-Disk SerialNumber in C# (no WMI)? 【发布时间】:2011-06-01 09:01:18 【问题描述】:我知道 CodeProject 中有两篇文章(一篇使用 WMI,另一篇没有 WMI,但使用 C++)。我试过WMI的方式,不仅慢,而且不可靠。所以,这就是为什么我决定不走这条路。我想通过 pInvoke 在 C# 中做到这一点。我试过了,但卡在 DeviceIoControl API 中。任何人都可以给我一个提示吗?这是我的代码:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Chemulator.Common
public class HDSerialNumber
[StructLayout(LayoutKind.Sequential)]
private 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)]
private struct SENDCMDINPARAMS
public Int32 cBufferSize;
public IDEREGS irDriveRegs;
public byte bDriveNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public Int32[] dwReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] bBuffer;
[StructLayout(LayoutKind.Sequential)]
private struct DRIVERSTATUS
public byte bDriverError;
public byte bIDEError;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public Int32[] dwReserved;
[StructLayout(LayoutKind.Sequential)]
private struct SENDCMDOUTPARAMS
public Int32 cBufferSize;
public DRIVERSTATUS DriverStatus;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IDENTIFY_BUFFER_SIZE)]
public byte[] bBuffer;
[StructLayout(LayoutKind.Sequential)]
private struct GETVERSIONOUTPARAMS
public byte bVersion;
public byte bRevision;
public byte bReserved;
public byte bIDEDeviceMap;
public Int32 fCapabilities;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public Int32 dwReserved;
[StructLayout(LayoutKind.Sequential)]
private struct STORAGE_PROPERTY_QUERY
public Int32 PropertyId;
public Int32 QueryType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] AdditionalParameters;
[StructLayout(LayoutKind.Sequential)]
private struct STORAGE_DEVICE_DESCRIPTOR
public Int32 Version;
public Int32 Size;
public byte DeviceType;
public byte DeviceTypeModifier;
public byte RemovableMedia;
public byte CommandQueueing;
public Int32 VendorIdOffset;
public Int32 ProductIdOffset;
public Int32 ProductRevisionOffset;
public Int32 SerialNumberOffset;
public byte BusType;
public Int32 RawPropertiesLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)]
public byte[] RawDeviceProperties;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, Int32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32")]
private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, ref uint lpBytesReturned, IntPtr lpOverlapped);
private const Int32 OPEN_EXISTING = 3;
private const Int32 GENERIC_READ = unchecked((int)0x80000000);
private const Int32 GENERIC_WRITE = 0x40000000;
private const Int32 FILE_SHARE_READ = 0x1;
private const Int32 FILE_SHARE_WRITE = 0x2;
private const Int32 FILE_SHARE_DELETE = 0x4;
private const Int32 SMART_GET_VERSION = 0x74080;
private const Int32 SMART_RCV_DRIVE_DATA = 0x7C088;
private const Int32 ID_CMD = 0xEC;
private const Int32 IDENTIFY_BUFFER_SIZE = 512;
private const Int32 CAP_SMART_CMD = 0x4;
private const Int32 IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;
private const Int32 PropertyStandardQuery = 0;
private const Int32 StorageDeviceProperty = 0;
public static string GetSerialNumber(int diskNumber)
string str = GetSerialNumberUsingStorageQuery(diskNumber);
if (string.IsNullOrEmpty(str))
str = GetSerialNumberUsingSmart(diskNumber);
return str;
public static string GetSerialNumberUsingStorageQuery(int diskNumber)
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
uint iBytesReturned = 0;
var spq = new STORAGE_PROPERTY_QUERY();
var sdd = new STORAGE_DEVICE_DESCRIPTOR();
spq.PropertyId = StorageDeviceProperty;
spq.QueryType = PropertyStandardQuery;
if (DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, spq, (uint)Marshal.SizeOf(spq), sdd, (uint)Marshal.SizeOf(sdd), ref iBytesReturned, IntPtr.Zero))
throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)");
var result = new StringBuilder();
if (sdd.SerialNumberOffset > 0)
var rawDevicePropertiesOffset = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length;
int pos = sdd.SerialNumberOffset - rawDevicePropertiesOffset;
while (pos < iBytesReturned && sdd.RawDeviceProperties[pos] != 0)
result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1));
pos += 1;
return result.ToString();
public static string GetSerialNumberUsingSmart(int diskNumber)
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
if (IsSmartSupported(hDisk))
Int32 iBytesReturned = 0;
var sci = new SENDCMDINPARAMS();
var sco = new SENDCMDOUTPARAMS();
sci.irDriveRegs.bCommandReg = ID_CMD;
sci.bDriveNumber = (byte)diskNumber;
sci.cBufferSize = IDENTIFY_BUFFER_SIZE;
if (DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, sci, (uint)Marshal.SizeOf(sci), sco, (uint)Marshal.SizeOf(sco), ref iBytesReturned, IntPtr.Zero))
throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)");
var result = new StringBuilder();
for (int index = 20; index < 39; index += 2)
result.Append(Encoding.ASCII.GetString(sco.bBuffer, index + 1, 1));
result.Append(Encoding.ASCII.GetString(sco.bBuffer, index, 1));
return result.ToString();
return string.Empty;
private static Win32Exception CreateWin32Exception(Int32 errorCode, string context)
var win32Exception = new Win32Exception(errorCode);
win32Exception.Data["Context"] = context;
return win32Exception;
private static SafeFileHandle OpenDisk(int diskNumber)
SafeFileHandle hDevice = CreateFile(string.Format(@"\\.\PhysicalDrive0", diskNumber), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (!hDevice.IsInvalid)
return hDevice;
else
throw CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile");
private static bool IsSmartSupported(SafeFileHandle hDisk)
uint iBytesReturned = 0;
var gvo = new GETVERSIONOUTPARAMS();
IntPtr pGVO = Marshal.AllocHGlobal(512);
if (DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, pGVO, 512, ref iBytesReturned, IntPtr.Zero))
return false;
return (gvo.fCapabilities & CAP_SMART_CMD) > 0;
【问题讨论】:
卡住了,因为你的电脑爆炸了,房子也被烧毁了,或者是什么问题? 为什么没有 WMI?和好奇心一样...... @Flavio:因为 WMI 速度慢且不可靠。 为什么不将 C++ 版本封装在 C++/CLI 类中,以便 C# 可以轻松调用它?这肯定会击败带有 p/invoke 注释的结构声明页面和页面,并尝试使它们与 C 结构布局兼容。 @Ben: ...因为我沉迷于“纯”C# :-) 【参考方案1】:查看有关 DeviceIOcontrol 的 pinvoke.net 教程。
向下滚动页面,您可以看到 VB .NET 3.0 完整示例(感谢“bogdandaniel”)由 pPumkiN 编辑。这是访问不同IO设备的完整示例。我相信也有 DRIVE_INFO。
我也没有这方面的经验。自己试试吧
【讨论】:
感谢您的链接。实际上,我的代码主要基于 VB 代码,我对 5 个重载版本的 DeviceIoControl 中的大量签名感到非常困惑。以上是关于如何在 C#(无 WMI)中获取硬盘序列号?的主要内容,如果未能解决你的问题,请参考以下文章