最初に言い訳。
C#ではコンソールにHello, World!を出したことがある程度の知識です。間違ってたら指摘してください。
とある掲示板でC#でHDDのセクタリードをするとは?という質問が出ていたのを見たので気になってやってみた。
ほぼここでやったことのC#版。ただし、MBRを読んでパーティションテーブルを取得した後パーティション1の先頭セクタを読んでいる。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace SectorReadSample
{
class SectorRead
{
[DllImport("Kernel32.dll")]
public static extern System.IntPtr CreateFile(System.Text.StringBuilder lpFileName,
uint dwDesiredAccess,
uint dwSharedMode,
System.IntPtr lpSecuriteAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
System.IntPtr hTemplateFile);
[DllImport("Kernel32.dll")]
unsafe public static extern bool ReadFile(System.IntPtr hFile,
byte* lpBuffer,
uint nNumberOfBytesToRead,
uint* lpNumberOfBytesRead,
System.IntPtr lpOverlapped);
[DllImport("Kernel32.dll")]
unsafe public static extern uint SetFilePointer(System.IntPtr hFile,
uint lDistanceToMove,
uint* lpDistanceToMoveHigh,
uint dwMoveMethod);
[DllImport("Kernel32.dll")]
public static extern bool CloseHandle(System.IntPtr hFile);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe public struct PartitionTable
{
public byte boot_flag; // ブートフラグ
public fixed byte start_chs[3]; // 開始CHS
public byte type; // パーティションの種類
public fixed byte end_chs[3]; // 終了CHS
public uint start_lba; // 開始LBA
public uint total_sector; // セクタ数
}
/* SetFilePointer用 */
public const uint FileBegin = 0;
public const uint FileCurrent = 1;
public const uint FileEnd = 2;
/* デバイス名 */
private System.Text.StringBuilder deviceName;
/* ハンドル */
private System.IntPtr hFile;
/* コンストラクタ */
public SectorRead(string dn)
{
deviceName = new System.Text.StringBuilder(dn);
hFile = CreateFile(deviceName,
0x80000000, // GENERIC_READ
0x00000001 | 0x00000002, // FILE_SHARE_READ | FILE_SHARE_WRITE
System.IntPtr.Zero, // NULL
3, // OPEN_EXISTING
0x00000080, // FILE_ATTRIBUTE_NORMAL
System.IntPtr.Zero); // NULL
}
/* デストラクタ */
~SectorRead()
{
CloseHandle(hFile);
}
/* セクタリード */
unsafe public void ReadSector(uint sector, byte[] buff)
{
uint len = 0;
fixed(byte* p = &buff[0])
{
SetFilePointer(hFile, sector * 0x200, &len, FileBegin);
ReadFile(hFile, p, 0x200, &len, IntPtr.Zero);
}
}
/* パーティションテーブル情報取得 MBR(セクタ0)を渡す */
unsafe public PartitionTable ReadPartitionTable(byte[] buff, int part)
{
PartitionTable result = new PartitionTable();
fixed(byte* p = &buff[446]) {
result = ((PartitionTable*)p)[part];
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
byte[] buff = new byte[512]; // 1セクタ分のバッファ
// Cドライブをオープン
SectorRead sr = new SectorRead("\\\\.\\PHYSICALDRIVE0");
// 先頭セクタ(MBR)を読み出す。
sr.ReadSector(0, buff);
// パーティション1の情報を取得
SectorRead.PartitionTable p1 = sr.ReadPartitionTable(buff, 0);
// パーティション1の先頭1セクタを読み出す
sr.ReadSector(p1.start_lba, buff);
/* パーティション1セクタ情報 */
Console.WriteLine("Partition Type:{0}", p1.type);
Console.WriteLine("Start LBA:{0}", p1.start_lba);
Console.WriteLine("Total Sector:{0}", p1.total_sector);
Console.WriteLine("Partition1: {0:X2} {1:X2} {2:X2} {3:X2} {4:X2} {5:X2} {6:X2}",
buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6]);
}
}
}
実行結果
Partition Type:7 (NTFSは7)
Start LBA:2048 (先頭セクタ)
Total Sector:625137664(総セクタ数 ・1K=1000換算で約320GB)
Partition1: EB 52 90 4E 54 46 53 (文字にすると.R.NTFS)
どうやら正しくパーティションが読めているようです。
C#ならC++より簡単に作れると思ったけど、この手のことをするにはC++の方が楽みたい。