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:
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
et qui suit est un script de conversion bidirectionnel en ligne
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