Joh*_*Doe 6 c# size ioctl disk deviceiocontrol
我使用下面的代码来获取物理磁盘大小,但返回的大小不正确.我用其他工具检查了尺寸.
以下代码报告
总磁盘空间: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;
}
Run Code Online (Sandbox Code Playgroud)
原生方法:
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;
}
Run Code Online (Sandbox Code Playgroud)
主要条目:
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);
}
Run Code Online (Sandbox Code Playgroud)
经过一番研究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;
}
}
Run Code Online (Sandbox Code Playgroud)
首先,我使用usingalias指令来进行代码的本机调用,就像在C/C++中一样.第一部分的重点是IoCtl.Execute方法.它是一种通用方法,类型是根据传递的第一个参数.它隐藏了使用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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
在IoCtl.CTL_CODE原本是在C/C++代码的宏,但是C#没有宏,所以我换了类似的声明DISK_GET_DRIVE_GEOMETRY_EX作为static readonly值,处理运行时间常数.IOCTL_删除了一些常量的前缀,因为有类名来限定它们.这一部分的最大特点是类CubicAddress,它是新定义的类的基础DiskGeometry.你可能想知道为什么甚至更多的疑惑.
CubicAddress事实上,该类是用于存储CHS address物理磁盘的简单类,并提供了一种从LBA格式转换地址的方法,我将其命名为Transform.虽然我从来没有听过有人将其命名CHS为立方体,但我认为像几何/体积这样的术语在数学和环绕物理磁盘中具有相同的用法.
CHS很可能,(x ,y, z),(R, G, B)或其他任何东西,你可以在一个立方体模型的方式他们.它们可能有一个寻址坐标,也可能用于描述几何,如矢量.因此,该课程CubicAddress有两个用法:
CHS/ LBA转换是线性变换/组合,并且我写仅Transform是用于LBA对CHS.参数geometry的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);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3789 次 |
| 最近记录: |