How to get Hard-Disk SerialNumber in C# (no WMI)?

断了今生、忘了曾经 提交于 2019-12-12 05:29:34

问题


I know there are two articles in CodeProject (one uses WMI and the other no WMI but in C++). I tried the WMI way, not only it's slow, but it is also unreliable. So, that's why I decided not to pursue that way. I want to do it in C# through pInvoke. I tried it but got stuck in DeviceIoControl API. Can anybody give me a hint? Here is my code:

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;
        }
    }
}

回答1:


Check out the pinvoke.net tutorial about DeviceIOcontrol.

Scroll down the page where you can see VB .NET 3.0 Full Example (Thanks to "bogdandaniel") Edited by pPumkiN. It is a complete example of accessing diffferent IO devices. I beleieve there is DRIVE_INFO too.

Also i dont have any experience with this. Try it yourself



来源:https://stackoverflow.com/questions/4559700/how-to-get-hard-disk-serialnumber-in-c-sharp-no-wmi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!