の研究をした後DeviceIocontrol
、ほとんどの時間はデザインに費やしています。ここでは、わかりやすくするために名前空間と部分クラスで区切られた2つの部分にコードを投稿します。これらをマージすることはできますが、個別に使用することはできません。
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
エイリアスディレクティブを使用して、C /C++のようにコードのネイティブ呼び出しを行います。最初の部分のポイントは、IoCtl.Execute
メソッドです。これはジェネリックメソッドであり、型は渡された最初の引数に従います。これは、メソッドを使用した構造体とポインターのマーシャリングの複雑さを隠しP/Invoke
ます。2番目のパラメーターは、に渡される目的の制御コードですDeviceIoControl
。3番目から最後までのパラメーターはとまったく同じ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
のように宣言を変更します。を修飾するクラス名があるため、のようないくつかの定数のプレフィックスは削除されます。この部分の最大のポイントはクラスです。これは、新しく定義されたクラスのベースです。なぜ、あるいはもっと疑問に思うかもしれません。static readonly
IOCTL_
CubicAddress
DiskGeometry
実際、このクラスは、物理ディスクを格納し、アドレスを私が名前を付けた形式から変換するメソッドを提供するためにCubicAddress
使用する単純なクラスです。誰かが立方体の名前を付けているのを聞いたことがありませんが、幾何学/体積のような用語は、数学と物理ディスクの周りで同じように使用されていると思います。CHS address
LBA
Transform
CHS
CHS
(x ,y, z)
、、(R, G, B)
または立方体の方法でそれらをモデル化できるその他のものである可能性があります。それらはアドレス指定のための座標を持っているかもしれません、そしてそれはまたベクトルのように幾何学を記述するために使われるかもしれません。したがって、このクラスCubicAddress
には2つの使用法があります。
- セクターの住所を表示します
- ジオメトリを記述します
CHS
/LBA
変換Transform
は線形変換/組み合わせであり、私はどちらがLBA
toであるかだけを書きました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);
}
}