I use the code below to get the physical disk size, but the size returned is not correct. I've checked the size with other tools.
The code below reports
Total disk space: 8.249.955.840 bytes
and it should be
Total disk space: 8.254.390.272 bytes
How can I retrieve the actual/correct physical disk size? Tested on USB drives and normal hard drives. The code is long, here separate it in parts to show.
The structure:
[StructLayout(LayoutKind.Sequential)]
internal struct DiskGeometry {
public long Cylinders;
public int MediaType;
public int TracksPerCylinder;
public int SectorsPerTrack;
public int BytesPerSector;
}
Native methods:
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;
}
Main entry:
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);
}
Your code calculates it in a wrong way. About the description of physical to logical sector number calculation, take a look of the artical on Wikipedia
and following is an online bidirectional conversion script
According to your post, the physical last sector would be at
And the size would be
Since you specify that it should be
8,254,390,272
, I calculate the last physical sector according to that size.255*63 is only for alignment, it's called cylinder boundary. Usually, the physical last sector is NOT end at the boundary, but for the reason not to access nonexistence sectors, it should be larger than
For example, if your physical last sector was as the calculated value above, then simply ignore the cylinder next to 1002, and use the sectors max to
chs(1002, 255, 63)
as your logical last sector would be safe.To get the physical disk size, you can invoke
DeviceIoControl
with the control codeIOCTL_DISK_GET_DRIVE_GEOMETRY_EX
. Here is the reference on MSDNAfter did some study of
DeviceIocontrol
, and most of time I spend on designing. Here I post the code in two parts, separated with namespace and partial classes for the clarity, you can merge them but cannot use them individually.First off, I use the
using
alias directive to make native calls of code more like in C/C++. The point of the first part, isIoCtl.Execute
method. It is a generic method and the type is according to the first argument passed. It hides the complexity of marshalling structures and pointers withP/Invoke
methods. The second parameter is the desired control code which will pass toDeviceIoControl
. From the third to the last parameter are exact the same asCreateFile
, and all have default value, they are optional.Following is the next part of code, and might have more things to mention.
The
IoCtl.CTL_CODE
is originally a macro in C/C++ code, but c# doesn't have macros, so that I change the declaration likeDISK_GET_DRIVE_GEOMETRY_EX
asstatic readonly
values, treated as runtime constants. Prefix of some constants likeIOCTL_
are removed, because there're class names to qualify them. The biggest point of this part would be the classCubicAddress
, it's the base of newly defined classDiskGeometry
. You might wonder of why or even more of wondering.The class
CubicAddress
is, in fact, a simple class use to storeCHS address
of phisical disks and provide a method converting the address fromLBA
format, which I namedTransform
. Although I never heard somebody names theCHS
as something cubic, but I think the terms like geometry/volumes are have the same usage in mathematics and arround physical disks.CHS
is likely,(x ,y, z)
,(R, G, B)
or any other things which you can model them in a cubic manner. They might have a coordinate for addressing, which may also used to describe the geometry, like a vector. Thus, the classCubicAddress
has two usages:CHS
/LBA
conversions are linear transformation/combination, and I wrote onlyTransform
which is forLBA
toCHS
. The parametergeometry
ofTransform
is the geometry referenced for the transformation, it's required because of a linear address can be transformed to a different coordinate with a different geometry.About naming, representing of the terms like
SectorsPerTrack
should be in a plural form likeSectors
. However, because of the dual usage ofCubicAddress
, I rather use the singular form.Finally, here's the test class