如何在 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)中获取硬盘序列号?的主要内容,如果未能解决你的问题,请参考以下文章

无标题

c# 获取移动硬盘信息监听移动设备的弹出与插入事件

如何在 .NET 中不使用 WMI 找到硬盘设备序列号?

使用 c# 如何提取有关本地计算机上存在的硬盘驱动器的信息

c# WMI获取机器硬件信息(硬盘,cpu,内存等)

使用WMI编程获取主机硬件信息(CPU_ID,硬盘主板BIOS序列号,Mac地址)