1

我知道 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;
        }
    }
}
4

1 回答 1

1

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

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

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

于 2010-12-30T04:25:33.700 回答