Размер физического диска неправильный (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