6

以下のコードを使用して物理ディスクのサイズを取得しましたが、返されるサイズが正しくありません。他のツールでサイズを確認しました。

以下のコードは報告します

総ディスク容量:8.249.955.840バイト

そしてそれは

総ディスク容量: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);
}
4

2 に答える 2

7

の研究をした後DeviceIocontrol、ほとんどの時間はデザインに費やしています。ここでは、わかりやすくするために名前空間と部分クラスで区切られた2つの部分にコードを投稿します。これらをマージすることはできますが、個別に使用することはできません

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エイリアスディレクティブを使用して、C /C++のようにコードのネイティブ呼び出しを行います。最初の部分のポイントは、IoCtl.Executeメソッドです。これはジェネリックメソッドであり、型は渡された最初の引数に従います。これは、メソッドを使用した構造体とポインターのマーシャリングの複雑さを隠しP/Invokeます。2番目のパラメーターは、に渡される目的の制御コードですDeviceIoControl。3番目から最後までのパラメーターはとまったく同じ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のように宣言を変更します。を修飾するクラス名があるため、のようないくつかの定数のプレフィックスは削除されます。この部分の最大のポイントはクラスです。これは、新しく定義されたクラスのベースです。なぜ、あるいはもっと疑問に思うかもしれません。static readonlyIOCTL_CubicAddressDiskGeometry

実際、このクラスは、物理ディスクを格納し、アドレスを私が名前を付けた形式から変換するメソッドを提供するためにCubicAddress使用する単純なクラスです。誰かが立方体の名前を付けているのを聞いたことがありませんが、幾何学/体積のような用語は、数学と物理ディスクの周りで同じように使用されていると思います。CHS addressLBATransformCHS

CHS(x ,y, z)、、(R, G, B)または立方体の方法でそれらをモデル化できるその他のものである可能性があります。それらはアドレス指定のための座標を持っているかもしれません、そしてそれはまたベクトルのように幾何学を記述するために使われるかもしれません。したがって、このクラスCubicAddressには2つの使用法があります。

  • セクターの住所を表示します
  • ジオメトリを記述します

CHS/LBA変換Transformは線形変換/組み合わせであり、私はどちらがLBAtoであるかだけを書きましたCHS。のパラメータgeometryは、変換で参照されるジオメトリです。線形アドレスは、異なるジオメトリを使用して別の座標に変換できるTransformため、これが必要です。

ネーミングについては、のような用語の表現は、のような複数SectorsPerTrackである必要があります。ただし、の二重使用のため、私はむしろ単数形を使用します。SectorsCubicAddress

最後に、これがテストクラスです

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);
    }
}
于 2013-02-25T18:15:07.800 に答える
1

あなたのコードはそれを間違った方法で計算します。物理的から論理的なセクター数の計算の説明については、ウィキペディアの記事をご覧ください

以下はオンライン双方向変換スクリプトです

あなたの投稿によると、物理的な最後のセクターは

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の横の円柱を無視しchs(1002, 255, 63)、論理的な最後のセクターが安全であるため、セクターmaxtoを使用します。

DeviceIoControl物理ディスクサイズを取得するには、制御コードを使用して呼び出すことができますIOCTL_DISK_GET_DRIVE_GEOMETRY_EX。これがMSDNのリファレンスです

于 2013-02-24T13:27:25.313 に答える