【问题标题】:How to get Hard-Disk SerialNumber in C# (no WMI)?如何在 C#(无 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(@"\\.\PhysicalDrive{0}", 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# :-)

标签: c# api hard-drive serial-number


【解决方案1】:

查看有关 DeviceIOcontrol 的 pinvoke.net 教程。

向下滚动页面,您可以看到 VB .NET 3.0 完整示例(感谢“bogdandaniel”)由 pPumkiN 编辑。这是访问不同IO设备的完整示例。我相信也有 DRIVE_INFO。

我也没有这方面的经验。自己试试吧

【讨论】:

  • 感谢您的链接。实际上,我的代码主要基于 VB 代码,我对 5 个重载版本的 DeviceIoControl 中的大量签名感到非常困惑。
猜你喜欢
  • 1970-01-01
  • 2013-02-11
  • 2010-11-24
  • 1970-01-01
  • 2011-05-04
  • 2010-12-06
  • 2016-08-28
  • 1970-01-01
  • 2011-01-02
相关资源
最近更新 更多