La taille du disque physique n’est pas correcte (IoCtlDiskGetDriveGeometry)

J’utilise le code ci-dessous pour obtenir la taille du disque physique , mais la taille renvoyée n’est pas correcte. J’ai vérifié la taille avec d’autres outils.

Le code ci-dessous rapporte

Espace disque total: 8.249.955.840 octets

et il devrait être

Espace disque total: 8.254.390.272 octets

Comment puis-je récupérer la taille réelle / correcte du disque physique? Testé sur des clés USB et des disques durs normaux. Le code est long, séparez-le ici en plusieurs parties.

La structure:

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

Méthodes natives:

 internal static class NativeMethods { [DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] public static extern SafeFileHandle CreateFile( ssortingng fileName, uint fileAccess, uint fileShare, IntPtr securityAtsortingbutes, 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; } 

Entrée principale:

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

Après avoir étudié DeviceIocontrol , je passe la plupart du temps à la conception. Ici, je poste le code en deux parties, séparées par un espace de noms et des classes partielles pour plus de clarté, vous pouvez les fusionner, mais vous ne pouvez pas les utiliser individuellement .

 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=Ssortingng; public static partial class IoCtl /* methods */ { [DllImport("kernel32.dll", SetLastError=true)] static extern SafeFileHandle CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAtsortingbutes, DWORD dwCreationDisposition, DWORD dwFlagsAndAtsortingbutes, 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( ref T x, DWORD dwIoControlCode, LPCTSTR lpFileName, DWORD dwDesiredAccess=GENERIC_READ, DWORD dwShareMode=FILE_SHARE_WRITE|FILE_SHARE_READ, LPSECURITY_ATTRIBUTES lpSecurityAtsortingbutes=default(LPSECURITY_ATTRIBUTES), DWORD dwCreationDisposition=OPEN_EXISTING, DWORD dwFlagsAndAtsortingbutes=0, HANDLE hTemplateFile=default(IntPtr) ) { using( var hDevice= CreateFile( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAtsortingbutes, dwCreationDisposition, dwFlagsAndAtsortingbutes, 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 Ssortingng 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; } } 

Tout d’abord, j’utilise la directive using alias pour faire des appels de code natifs plus comme en C / C ++. Le sharepoint la première partie est la méthode IoCtl.Execute . C’est une méthode générique et le type correspond au premier argument passé. Il cache la complexité des structures de sortingage et des pointeurs avec les méthodes P/Invoke . Le deuxième paramètre est le code de contrôle souhaité qui sera transmis à DeviceIoControl . Du troisième au dernier paramètre sont exactement les mêmes que CreateFile , et ont tous une valeur par défaut, ils sont facultatifs .

Ce qui suit est la prochaine partie du code, et pourrait avoir plus de choses à mentionner.

 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=Ssortingng; 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(Ssortingng deviceName) { return new DiskGeometry(deviceName); } DiskGeometry(Ssortingng 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 est à l’origine une macro dans le code C / C ++, mais c # n’a pas de macros. Par conséquent, je modifie la déclaration comme DISK_GET_DRIVE_GEOMETRY_EX tant que valeurs static readonly , traitées comme des constantes d’exécution . Les préfixes de certaines constantes comme IOCTL_ sont supprimés, car il existe des noms de classes pour les qualifier. Le point le plus important de cette partie serait la classe CubicAddress , c’est la base de la classe DiskGeometry nouvellement définie. Vous pourriez vous demander pourquoi ou même plus de demander.

La classe CubicAddress est en fait une simple classe permettant de stocker l’ CHS address des disques physiques et de fournir une méthode permettant de convertir l’adresse du format LBA , que j’ai nommée Transform . Bien que je n’aie jamais entendu dire que la CHS soit CHS par quelque chose de cubique, je pense que les termes géomésortinge / volumes ont le même usage en mathématiques et en disques physiques.

CHS est probable, (x ,y, z) , (R, G, B) ou toute autre chose que vous pouvez modéliser de manière cubique. Ils peuvent avoir une coordonnée pour l’adressage, qui peut également être utilisée pour décrire la géomésortinge, comme un vecteur . Ainsi, la classe CubicAddress a deux usages:

  • présente une adresse de secteur
  • décrit la géomésortinge

CHS conversions CHS / LBA sont des transformations / combinaisons linéaires, et j’ai écrit uniquement Transform qui est pour LBA vers CHS . Le paramètre geometry de Transform est la géomésortinge référencée pour la transformation. Il est requirejs car une adresse linéaire peut être transformée en une coordonnée différente avec une géomésortinge différente .

A propos de nommer, représentant des termes comme SectorsPerTrack devrait être au pluriel comme Sectors . Cependant, en raison de la double utilisation de CubicAddress , j’utilise plutôt la forme singulière.

Enfin, voici la classe de test

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

Votre code le calcule de manière incorrecte . À propos de la description du calcul du nombre de secteurs physiques à logiques, jetez un coup d’œil à l’article sur Wikipedia

  • Adressage de bloc logique

et qui suit est un script de conversion bidirectionnel en ligne

  • Conversion CHS / LBA

Selon votre message, le dernier secteur physique serait à

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

Et la taille serait

total secteurs = 1 + 16121855 = 16121856 secteurs

16121856 * 512 octets par secteur = 8254390272 octets

Puisque vous spécifiez qu’il devrait s’agir de 8 8,254,390,272 , je calcule le dernier secteur physique en fonction de cette taille.

255 * 63 sert uniquement à l’ alignement , il est appelé limite du cylindre . Habituellement, le dernier secteur physique ne se termine PAS à la frontière, mais pour ne pas accéder aux secteurs de non-existence , il doit être plus grand que

[nombre total de cylindres] * [pistes par cylindre (également têtes)] * [secteurs par piste]

Par exemple, si votre dernier secteur physique correspond à la valeur calculée ci-dessus, ignorez simplement le cylindre situé à côté de 1002 et utilisez les secteurs max à chs(1002, 255, 63) car votre dernier secteur logique serait sûr.

Pour obtenir la taille du disque physique, vous pouvez appeler DeviceIoControl avec le code de contrôle IOCTL_DISK_GET_DRIVE_GEOMETRY_EX . Voici la référence sur MSDN

  • Code de contrôle IOCTL_DISK_GET_DRIVE_GEOMETRY_EX (Windows)