Размер физического диска неправильный (IoCtlDiskGetDriveGeometry)

Я использую код ниже, чтобы получить физический размер диска, но возвращенный размер неверен. Я проверил размер с помощью других инструментов.

код ниже сообщает

общее дисковое пространство: 8.249.955.840 байт

и он должен быть!--6-->

общее дисковое пространство: 8.254.390.272 байт

как я могу получить фактический/физический диск размер? протестировано на USB-накопителях и обычных жестких дисках. Код длинный, здесь разделите его на части, чтобы показать.

структура:

[StructLayout(LayoutKind.Sequential)]
internal struct DiskGeometry {
    public long Cylinders;
    public int MediaType;
    public int TracksPerCylinder;
    public int SectorsPerTrack;
    public int BytesPerSector;
}

собственные методы:

internal static class NativeMethods {
    [DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern SafeFileHandle CreateFile(
        string fileName,
        uint fileAccess,
        uint fileShare,
        IntPtr securityAttributes,
        uint creationDisposition,
        uint flags,
        IntPtr template
        );

    [DllImport("Kernel32.dll", SetLastError=false, CharSet=CharSet.Auto)]
    public static extern int DeviceIoControl(
        SafeFileHandle device,
        uint controlCode,
        IntPtr inBuffer,
        uint inBufferSize,
        IntPtr outBuffer,
        uint outBufferSize,
        ref uint bytesReturned,
        IntPtr overlapped
        );

    internal const uint FileAccessGenericRead=0x80000000;
    internal const uint FileShareWrite=0x2;
    internal const uint FileShareRead=0x1;
    internal const uint CreationDispositionOpenExisting=0x3;
    internal const uint IoCtlDiskGetDriveGeometry=0x70000;
}

Главная запись:

internal const uint IoCtlDiskGetDriveGeometry=0x70000;

public static void Main() {
    SafeFileHandle diskHandle=
        NativeMethods.CreateFile(
            @".PhysicalDrive0",
            NativeMethods.FileAccessGenericRead,
            NativeMethods.FileShareWrite|NativeMethods.FileShareRead,
            IntPtr.Zero,
            NativeMethods.CreationDispositionOpenExisting,
            0,
            IntPtr.Zero
            );

    if(diskHandle.IsInvalid) {
        Console.WriteLine("CreateFile failed with error: {0}", Marshal.GetLastWin32Error());
        return;
    }

    int geometrySize=Marshal.SizeOf(typeof(DiskGeometry));
    Console.WriteLine("geometry size = {0}", geometrySize);

    IntPtr geometryBlob=Marshal.AllocHGlobal(geometrySize);
    uint numBytesRead=0;

    if(
        0==NativeMethods.DeviceIoControl(
            diskHandle,
            NativeMethods.IoCtlDiskGetDriveGeometry,
            IntPtr.Zero,
            0,
            geometryBlob,
            (uint)geometrySize,
            ref numBytesRead,
            IntPtr.Zero
            )
        ) {
        Console.WriteLine(
            "DeviceIoControl failed with error: {0}",
            Marshal.GetLastWin32Error()
            );

        return;
    }

    Console.WriteLine("Bytes read = {0}", numBytesRead);

    DiskGeometry geometry=(DiskGeometry)Marshal.PtrToStructure(geometryBlob, typeof(DiskGeometry));
    Marshal.FreeHGlobal(geometryBlob);

    long bytesPerCylinder=(long)geometry.TracksPerCylinder*(long)geometry.SectorsPerTrack*(long)geometry.BytesPerSector;
    long totalSize=geometry.Cylinders*bytesPerCylinder;

    Console.WriteLine("Media Type:           {0}", geometry.MediaType);
    Console.WriteLine("Cylinders:            {0}", geometry.Cylinders);
    Console.WriteLine("Tracks per Cylinder:  {0}", geometry.TracksPerCylinder);
    Console.WriteLine("Sectors per Track:    {0}", geometry.SectorsPerTrack);
    Console.WriteLine("Bytes per Sector:     {0}", geometry.BytesPerSector);
    Console.WriteLine("Bytes per Cylinder:   {0}", bytesPerCylinder);
    Console.WriteLine("Total disk space:     {0}", totalSize);
}

2 ответов


после некоторого изучения DeviceIocontrol, и большую часть времени я провожу на проектирование. Здесь я размещаю код в двух частях, разделенных пространством имен и частичными классами для ясности, вы можете объединить их, но не может использовать их по отдельности.

namespace DiskManagement {
    using Microsoft.Win32.SafeHandles;

    using LPSECURITY_ATTRIBUTES=IntPtr;
    using LPOVERLAPPED=IntPtr;
    using LPVOID=IntPtr;
    using HANDLE=IntPtr;

    using LARGE_INTEGER=Int64;
    using DWORD=UInt32;
    using LPCTSTR=String;

    public static partial class IoCtl /* methods */ {
        [DllImport("kernel32.dll", SetLastError=true)]
        static extern SafeFileHandle CreateFile(
            LPCTSTR lpFileName,
            DWORD dwDesiredAccess,
            DWORD dwShareMode,
            LPSECURITY_ATTRIBUTES lpSecurityAttributes,
            DWORD dwCreationDisposition,
            DWORD dwFlagsAndAttributes,
            HANDLE hTemplateFile
            );

        [DllImport("kernel32.dll", SetLastError=true)]
        static extern DWORD DeviceIoControl(
            SafeFileHandle hDevice,
            DWORD dwIoControlCode,
            LPVOID lpInBuffer,
            DWORD nInBufferSize,
            LPVOID lpOutBuffer,
            int nOutBufferSize,
            ref DWORD lpBytesReturned,
            LPOVERLAPPED lpOverlapped
            );

        static DWORD CTL_CODE(DWORD DeviceType, DWORD Function, DWORD Method, DWORD Access) {
            return (((DeviceType)<<16)|((Access)<<14)|((Function)<<2)|(Method));
        }

        public static void Execute<T>(
            ref T x,
            DWORD dwIoControlCode,
            LPCTSTR lpFileName,
            DWORD dwDesiredAccess=GENERIC_READ,
            DWORD dwShareMode=FILE_SHARE_WRITE|FILE_SHARE_READ,
            LPSECURITY_ATTRIBUTES lpSecurityAttributes=default(LPSECURITY_ATTRIBUTES),
            DWORD dwCreationDisposition=OPEN_EXISTING,
            DWORD dwFlagsAndAttributes=0,
            HANDLE hTemplateFile=default(IntPtr)
            ) {
            using(
                var hDevice=
                    CreateFile(
                        lpFileName,
                        dwDesiredAccess, dwShareMode,
                        lpSecurityAttributes,
                        dwCreationDisposition, dwFlagsAndAttributes,
                        hTemplateFile
                        )
                ) {
                if(null==hDevice||hDevice.IsInvalid)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                var nOutBufferSize=Marshal.SizeOf(typeof(T));
                var lpOutBuffer=Marshal.AllocHGlobal(nOutBufferSize);
                var lpBytesReturned=default(DWORD);
                var NULL=IntPtr.Zero;

                var result=
                    DeviceIoControl(
                        hDevice, dwIoControlCode,
                        NULL, 0,
                        lpOutBuffer, nOutBufferSize,
                        ref lpBytesReturned, NULL
                        );

                if(0==result)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                x=(T)Marshal.PtrToStructure(lpOutBuffer, typeof(T));
                Marshal.FreeHGlobal(lpOutBuffer);
            }
        }
    }

    public enum MEDIA_TYPE: int {
        Unknown=0,
        F5_1Pt2_512=1,
        F3_1Pt44_512=2,
        F3_2Pt88_512=3,
        F3_20Pt8_512=4,
        F3_720_512=5,
        F5_360_512=6,
        F5_320_512=7,
        F5_320_1024=8,
        F5_180_512=9,
        F5_160_512=10,
        RemovableMedia=11,
        FixedMedia=12,
        F3_120M_512=13,
        F3_640_512=14,
        F5_640_512=15,
        F5_720_512=16,
        F3_1Pt2_512=17,
        F3_1Pt23_1024=18,
        F5_1Pt23_1024=19,
        F3_128Mb_512=20,
        F3_230Mb_512=21,
        F8_256_128=22,
        F3_200Mb_512=23,
        F3_240M_512=24,
        F3_32M_512=25
    }

    partial class DiskGeometry /* structures */ {
        [StructLayout(LayoutKind.Sequential)]
        struct DISK_GEOMETRY {
            internal LARGE_INTEGER Cylinders;
            internal MEDIA_TYPE MediaType;
            internal DWORD TracksPerCylinder;
            internal DWORD SectorsPerTrack;
            internal DWORD BytesPerSector;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct DISK_GEOMETRY_EX {
            internal DISK_GEOMETRY Geometry;
            internal LARGE_INTEGER DiskSize;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst=1)]
            internal byte[] Data;
        }
    }

    partial class DiskGeometry /* properties and fields */ {
        public MEDIA_TYPE MediaType {
            get {
                return m_Geometry.MediaType;
            }
        }

        public String MediaTypeName {
            get {
                return Enum.GetName(typeof(MEDIA_TYPE), this.MediaType);
            }
        }

        public override long Cylinder {
            get {
                return m_Geometry.Cylinders;
            }
        }

        public override uint Head {
            get {
                return m_Geometry.TracksPerCylinder;
            }
        }

        public override uint Sector {
            get {
                return m_Geometry.SectorsPerTrack;
            }
        }

        public DWORD BytesPerSector {
            get {
                return m_Geometry.BytesPerSector;
            }
        }

        public long DiskSize {
            get {
                return m_DiskSize;
            }
        }

        public long MaximumLinearAddress {
            get {
                return m_MaximumLinearAddress;
            }
        }

        public CubicAddress MaximumCubicAddress {
            get {
                return m_MaximumCubicAddress;
            }
        }

        public DWORD BytesPerCylinder {
            get {
                return m_BytesPerCylinder;
            }
        }

        CubicAddress m_MaximumCubicAddress;
        long m_MaximumLinearAddress;
        DWORD m_BytesPerCylinder;
        LARGE_INTEGER m_DiskSize;
        DISK_GEOMETRY m_Geometry;
    }
}

во-первых, я использую using директива Alias чтобы сделать собственные вызовы кода более похожими на C / C++. Смысл первой части -IoCtl.Execute метод. Это общий метод, и тип соответствует the первый аргумент прошло. Он скрывает сложность маршалинга структур и указателей с помощью P/Invoke методы. Второй параметр-желаемый код управления, который будет передаваться в DeviceIoControl. От третьего до последнего параметра точно такие же, как CreateFile, и все они имеют значение по умолчанию, они дополнительно.

Следующая часть кода, и может иметь больше вещей, чтобы упомянуть.

namespace DiskManagement {
    using Microsoft.Win32.SafeHandles;

    using LPSECURITY_ATTRIBUTES=IntPtr;
    using LPOVERLAPPED=IntPtr;
    using LPVOID=IntPtr;
    using HANDLE=IntPtr;

    using LARGE_INTEGER=Int64;
    using DWORD=UInt32;
    using LPCTSTR=String;

    partial class IoCtl /* constants */ {
        public const DWORD
            DISK_BASE=0x00000007,
            METHOD_BUFFERED=0,
            FILE_ANY_ACCESS=0;

        public const DWORD
            GENERIC_READ=0x80000000,
            FILE_SHARE_WRITE=0x2,
            FILE_SHARE_READ=0x1,
            OPEN_EXISTING=0x3;

        public static readonly DWORD DISK_GET_DRIVE_GEOMETRY_EX=
            IoCtl.CTL_CODE(DISK_BASE, 0x0028, METHOD_BUFFERED, FILE_ANY_ACCESS);

        public static readonly DWORD DISK_GET_DRIVE_GEOMETRY=
            IoCtl.CTL_CODE(DISK_BASE, 0, METHOD_BUFFERED, FILE_ANY_ACCESS);
    }

    public partial class CubicAddress {
        public static CubicAddress Transform(long linearAddress, CubicAddress geometry) {
            var cubicAddress=new CubicAddress();
            var sectorsPerCylinder=geometry.Sector*geometry.Head;
            long remainder;
            cubicAddress.Cylinder=Math.DivRem(linearAddress, sectorsPerCylinder, out remainder);
            cubicAddress.Head=(uint)Math.DivRem(remainder, geometry.Sector, out remainder);
            cubicAddress.Sector=1+(uint)remainder;
            return cubicAddress;
        }

        public virtual long Cylinder {
            get;
            set;
        }

        public virtual uint Head {
            get;
            set;
        }

        public virtual uint Sector {
            get;
            set;
        }
    }

    public partial class DiskGeometry: CubicAddress {
        internal static void ThrowIfDiskSizeOutOfIntegrity(long remainder) {
            if(0!=remainder) {
                var message="DiskSize is not an integral multiple of a sector size";
                throw new ArithmeticException(message);
            }
        }

        public static DiskGeometry FromDevice(String deviceName) {
            return new DiskGeometry(deviceName);
        }

        DiskGeometry(String deviceName) {
            var x=new DISK_GEOMETRY_EX();
            IoCtl.Execute(ref x, IoCtl.DISK_GET_DRIVE_GEOMETRY_EX, deviceName);
            m_DiskSize=x.DiskSize;
            m_Geometry=x.Geometry;

            long remainder;
            m_MaximumLinearAddress=Math.DivRem(DiskSize, BytesPerSector, out remainder)-1;
            ThrowIfDiskSizeOutOfIntegrity(remainder);

            m_BytesPerCylinder=BytesPerSector*Sector*Head;
            m_MaximumCubicAddress=DiskGeometry.Transform(m_MaximumLinearAddress, this);
        }
    }
}

на IoCtl.CTL_CODE - Это изначально макрос в коде C / C++, но c# не имеет макросов, поэтому я изменяю объявление как DISK_GET_DRIVE_GEOMETRY_EX as static readonly значения, рассматривается как константы времени выполнения. Префикс некоторых констант, таких как IOCTL_ удаляются, потому что есть имена классов, чтобы квалифицировать их. Самой большой точкой этой части будет class CubicAddress, это основа недавно определенного класса DiskGeometry. Вы можете задаться вопросом, почему или даже больше.

класс CubicAddress, на самом деле, простое использование класса в магазине CHS address физических дисков и предоставить метод преобразования адреса из LBA формат, который я назвал Transform. Хотя я никогда не слышал, чтобы кто-то называет CHS как что-то кубическое, но я думаю, что такие термины, как геометрия/тома, имеют одинаковое использование в математике и вокруг физических дисков.

CHS скорее всего, (x ,y, z), (R, G, B) или любые другие вещи, которые вы можете моделировать их в кубической манере. У них может быть координации для адресация, которая также может использоваться для описания геометрии, например вектор. Таким образом, класс CubicAddress имеет два варианта использования:

  • представляет адрес сектора
  • описывает геометрия

CHS/LBA преобразование являются линейным преобразованием / комбинацией, и я написал только Transform, который является для LBA to CHS. Параметр geometry of Transform геометрии ссылками для преобразование, это требуется из-за линейный адрес может быть преобразован в другую координату с другой геометрией.

об именовании, представлении таких терминов, как SectorsPerTrack должно быть МН форму Sectors. Однако, из-за двойного использования CubicAddress, Я скорее использую единственную форму.

наконец, вот тестовый класс

public partial class TestClass {
    public static void TestMethod() {
        var diskGeometry=DiskGeometry.FromDevice(@"\.\PhysicalDrive3");
        var cubicAddress=diskGeometry.MaximumCubicAddress;

        Console.WriteLine("            media type: {0}", diskGeometry.MediaTypeName);
        Console.WriteLine();

        Console.WriteLine("maximum linear address: {0}", diskGeometry.MaximumLinearAddress);
        Console.WriteLine("  last cylinder number: {0}", cubicAddress.Cylinder);
        Console.WriteLine("      last head number: {0}", cubicAddress.Head);
        Console.WriteLine("    last sector number: {0}", cubicAddress.Sector);
        Console.WriteLine();

        Console.WriteLine("             cylinders: {0}", diskGeometry.Cylinder);
        Console.WriteLine("   tracks per cylinder: {0}", diskGeometry.Head);
        Console.WriteLine("     sectors per track: {0}", diskGeometry.Sector);
        Console.WriteLine();

        Console.WriteLine("      bytes per sector: {0}", diskGeometry.BytesPerSector);
        Console.WriteLine("    bytes per cylinder: {0}", diskGeometry.BytesPerCylinder);
        Console.WriteLine("      total disk space: {0}", diskGeometry.DiskSize);
    }
}

ваш код вычисляет его неправильно. О описании физического к логическому расчету номера сектора, взгляните на статью в Википедии

и ниже приведен онлайн-скрипт двунаправленного преобразования

согласно вашему сообщению, физический последний сектор быть

chs(1003, 137, 30) = ((1003 * 255) + 137) * 63 + 30 - 1 = lba (16121855)

и размер будет

всего секторов = 1+16121855 = 16121856 секторов

16121856 * 512 байт на сектор = 8254390272 байт

так как вы указываете, что это должно быть 8,254,390,272, я вычисляю последний физический сектор в соответствии с этим размером.

255*63 только для трассы, это называется цилиндр границей. Обычно физический последний сектор не конец на границе, но по причине не доступа небытие сектора, это должно быть больше, чем

[общее количество цилиндров] *[дорожки на цилиндр (также головки)] * [сектора на дорожку]

например, если ваш физический последний сектор был как вычисленное значение выше, то просто игнорировать цилиндр рядом со 1002, и используйте сектора max to chs(1002, 255, 63) поскольку ваш логический последний сектор будет безопасным.

чтобы получить размер физического диска, вы можете вызвать DeviceIoControl С кодом управления IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. Вот ссылка на MSDN