使用 C# 在 USB 上创建多个分区
Creating multiple partitions on USB using C#
我正在尝试使用 DeviceIOControl 在 USB 中创建多个分区。它总是只创建一个分区。
这是我的源代码
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32")]
static extern int CloseHandle(IntPtr handle);
[DllImport("kernel32")]
private static extern int DeviceIoControl
(IntPtr deviceHandle, uint ioControlCode,
IntPtr inBuffer, int inBufferSize,
IntPtr outBuffer, int outBufferSize,
ref int bytesReturned, IntPtr overlapped);
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const uint OPEN_EXISTING = 0x00000003;
public const uint FILE_ATTRIBUTE_NORMAL = 0x80;
public const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x90083;
public const int DRIVE_ACCESS_RETRIES = 10;
public const int DRIVE_ACCESS_TIMEOUT = 15000;
public const uint FSCTL_LOCK_VOLUME = 0x00090018;
static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
public const uint IOCTL_DISK_GET_DRIVE_LAYOUT_EX = 0x00070050;
public const uint IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = 0x000700A0;
public const uint IOCTL_DISK_CREATE_DISK = 0x0007C058;
public const uint IOCTL_DISK_UPDATE_PROPERTIES = 0x00070140;
public const uint IOCTL_DISK_SET_DRIVE_LAYOUT_EX = 0x0007C054;
public const int MIN_EXTRA_PART_SIZE = 1024 * 1024;
public static int mediaType = 0;
/// <summary>
/// Describes the geometry of disk devices and media.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct DISK_GEOMETRY
{
/// <summary>
/// The number of cylinders.
/// </summary>
[FieldOffset(0)]
public Int64 Cylinders;
/// <summary>
/// The type of media. For a list of values, see MEDIA_TYPE.
/// </summary>
[FieldOffset(8)]
public MEDIA_TYPE MediaType;
/// <summary>
/// The number of tracks per cylinder.
/// </summary>
[FieldOffset(12)]
public uint TracksPerCylinder;
/// <summary>
/// The number of sectors per track.
/// </summary>
[FieldOffset(16)]
public uint SectorsPerTrack;
/// <summary>
/// The number of bytes per sector.
/// </summary>
[FieldOffset(20)]
public uint BytesPerSector;
}
/// <summary>
/// Describes the extended geometry of disk devices and media.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DISK_GEOMETRY_EX
{
/// <summary>
/// A DISK_GEOMETRY structure.
/// </summary>
[FieldOffset(0)]
public DISK_GEOMETRY Geometry;
/// <summary>
/// The disk size, in bytes.
/// </summary>
[FieldOffset(24)]
public Int64 DiskSize;
/// <summary>
/// Any additional data.
/// </summary>
[FieldOffset(32)]
public Byte Data;
}
/// <summary>
/// Represents the format of a partition.
/// </summary>
public enum PARTITION_STYLE : uint
{
/// <summary>
/// Master boot record (MBR) format.
/// </summary>
PARTITION_STYLE_MBR = 0,
/// <summary>
/// GUID Partition Table (GPT) format.
/// </summary>
PARTITION_STYLE_GPT = 1,
/// <summary>
/// Partition not formatted in either of the recognized formats—MBR or GPT.
/// </summary>
PARTITION_STYLE_RAW = 2
}
/// <summary>
/// Contains partition information specific to master boot record (MBR) disks.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct PARTITION_INFORMATION_MBR
{
#region Constants
/// <summary>
/// An unused entry partition.
/// </summary>
public const byte PARTITION_ENTRY_UNUSED = 0x00;
/// <summary>
/// A FAT12 file system partition.
/// </summary>
public const byte PARTITION_FAT_12 = 0x01;
/// <summary>
/// A FAT16 file system partition.
/// </summary>
public const byte PARTITION_FAT_16 = 0x04;
/// <summary>
/// An extended partition.
/// </summary>
public const byte PARTITION_EXTENDED = 0x05;
/// <summary>
/// An IFS partition.
/// </summary>
public const byte PARTITION_IFS = 0x07;
/// <summary>
/// A FAT32 file system partition.
/// </summary>
public const byte PARTITION_FAT32 = 0x0B;
/// <summary>
/// A logical disk manager (LDM) partition.
/// </summary>
public const byte PARTITION_LDM = 0x42;
/// <summary>
/// An NTFT partition.
/// </summary>
public const byte PARTITION_NTFT = 0x80;
/// <summary>
/// A valid NTFT partition.
///
/// The high bit of a partition type code indicates that a partition is part of an NTFT mirror or striped array.
/// </summary>
public const byte PARTITION_VALID_NTFT = 0xC0;
#endregion
/// <summary>
/// The type of partition. For a list of values, see Disk Partition Types.
/// </summary>
[FieldOffset(0)]
[MarshalAs(UnmanagedType.U1)]
public byte PartitionType;
/// <summary>
/// If this member is TRUE, the partition is bootable.
/// </summary>
[FieldOffset(1)]
[MarshalAs(UnmanagedType.I1)]
public bool BootIndicator;
/// <summary>
/// If this member is TRUE, the partition is of a recognized type.
/// </summary>
[FieldOffset(2)]
[MarshalAs(UnmanagedType.I1)]
public bool RecognizedPartition;
/// <summary>
/// The number of hidden sectors in the partition.
/// </summary>
[FieldOffset(4)]
public uint HiddenSectors;
}
/// <summary>
/// Contains GUID partition table (GPT) partition information.
/// </summary>
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct PARTITION_INFORMATION_GPT
{
/// <summary>
/// A GUID that identifies the partition type.
///
/// Each partition type that the EFI specification supports is identified by its own GUID, which is
/// published by the developer of the partition.
/// </summary>
[FieldOffset(0)]
public Guid PartitionType;
/// <summary>
/// The GUID of the partition.
/// </summary>
[FieldOffset(16)]
public Guid PartitionId;
/// <summary>
/// The Extensible Firmware Interface (EFI) attributes of the partition.
///
/// </summary>
[FieldOffset(32)]
public UInt64 Attributes;
/// <summary>
/// A wide-character string that describes the partition.
/// </summary>
[FieldOffset(40)]
public string Name;
}
/// <summary>
/// Provides information about a drive's master boot record (MBR) partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_MBR
{
/// <summary>
/// The signature of the drive.
/// </summary>
[FieldOffset(0)]
public uint Signature;
}
/// <summary>
/// Contains information about a drive's GUID partition table (GPT) partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_GPT
{
/// <summary>
/// The GUID of the disk.
/// </summary>
[FieldOffset(0)]
public Guid DiskId;
/// <summary>
/// The starting byte offset of the first usable block.
/// </summary>
[FieldOffset(16)]
public Int64 StartingUsableOffset;
/// <summary>
/// The size of the usable blocks on the disk, in bytes.
/// </summary>
[FieldOffset(24)]
public Int64 UsableLength;
/// <summary>
/// The maximum number of partitions that can be defined in the usable block.
/// </summary>
[FieldOffset(32)]
public uint MaxPartitionCount;
}
/// <summary>
/// Contains information about a disk partition.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct PARTITION_INFORMATION_EX
{
/// <summary>
/// The format of the partition. For a list of values, see PARTITION_STYLE.
/// </summary>
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
/// <summary>
/// The starting offset of the partition.
/// </summary>
[FieldOffset(8)]
public Int64 StartingOffset;
/// <summary>
/// The length of the partition, in bytes.
/// </summary>
[FieldOffset(16)]
public Int64 PartitionLength;
/// <summary>
/// The number of the partition (1-based).
/// </summary>
[FieldOffset(24)]
public uint PartitionNumber;
/// <summary>
/// If this member is TRUE, the partition information has changed. When you change a partition (with
/// IOCTL_DISK_SET_DRIVE_LAYOUT), the system uses this member to determine which partitions have changed
/// and need their information rewritten.
/// </summary>
[FieldOffset(28)]
[MarshalAs(UnmanagedType.I1)]
public bool RewritePartition;
/// <summary>
/// A PARTITION_INFORMATION_MBR structure that specifies partition information specific to master boot
/// record (MBR) disks. The MBR partition format is the standard AT-style format.
/// </summary>
[FieldOffset(32)]
public PARTITION_INFORMATION_MBR Mbr;
/// <summary>
/// A PARTITION_INFORMATION_GPT structure that specifies partition information specific to GUID partition
/// table (GPT) disks. The GPT format corresponds to the EFI partition format.
/// </summary>
[FieldOffset(32)]
public PARTITION_INFORMATION_GPT Gpt;
}
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_UNION
{
[FieldOffset(0)]
public DRIVE_LAYOUT_INFORMATION_MBR Mbr;
[FieldOffset(0)]
public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
}
/// <summary>
/// Contains extended information about a drive's partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_EX
{
/// <summary>
/// The style of the partitions on the drive enumerated by the PARTITION_STYLE enumeration.
/// </summary>
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
/// <summary>
/// The number of partitions on a drive.
///
/// On disks with the MBR layout, this value is always a multiple of 4. Any partitions that are unused have
/// a partition type of PARTITION_ENTRY_UNUSED.
/// </summary>
[FieldOffset(4)]
public uint PartitionCount;
/// <summary>
/// A DRIVE_LAYOUT_INFORMATION_MBR structure containing information about the master boot record type
/// partitioning on the drive.
/// </summary>
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_UNION Mbr;
/// <summary>
/// A DRIVE_LAYOUT_INFORMATION_GPT structure containing information about the GUID disk partition type
/// partitioning on the drive.
/// </summary>
// [FieldOffset(8)]
//public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
/// <summary>
/// A variable-sized array of PARTITION_INFORMATION_EX structures, one structure for each partition on the
/// drive.
/// </summary>
[FieldOffset(48)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 4)]
public PARTITION_INFORMATION_EX[] PartitionEntry;
}
[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK_MBR
{
[FieldOffset(0)]
public uint Signature;
}
[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK_GPT
{
[FieldOffset(0)]
public Guid DiskId;
[FieldOffset(16)]
public uint MaxPartitionCount;
}
[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
[FieldOffset(4)]
public CREATE_DISK_MBR Mbr;
[FieldOffset(4)]
public CREATE_DISK_GPT Gpt;
}
static IntPtr GetHandle(int driveIndex)
{
IntPtr handle;
//bool locked = false;
Program p = new Program();
string physicalName = p.GetPhysicalName(driveIndex);
Debug.WriteLine(physicalName);
handle = CreateFile(physicalName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (handle == INVALID_HANDLE_VALUE)
{
Debug.WriteLine(Marshal.GetLastWin32Error());
return IntPtr.Zero;
}
return handle;
}
//Returns true if drive Index is successfully created
//Returns false if not created successfully
static bool CreatePartition(int driveIndex)
{
IntPtr handle = GetHandle(driveIndex);
if (handle == INVALID_HANDLE_VALUE)
{
return false;
}
//Step 2: IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to get the physical disk's geometry ( we need some information in it to fill partition data)
//The number of surfaces (or heads, which is the same thing), cylinders, and sectors vary a lot; the specification of the number of each is called the geometry of a hard disk.
//The geometry is usually stored in a special, battery-powered memory location called the CMOS RAM , from where the operating system can fetch it during bootup or driver initialization.
int size = 0;
DISK_GEOMETRY_EX geometry = new DISK_GEOMETRY_EX();
IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)));
Marshal.StructureToPtr(geometry, lpOutBuffer, false);
int result = DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, IntPtr.Zero, 0, lpOutBuffer,
Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)),
ref size, IntPtr.Zero);
geometry = (DISK_GEOMETRY_EX)Marshal.PtrToStructure(lpOutBuffer, typeof(DISK_GEOMETRY_EX));
//Step 3: IOCTL_DISK_CREATE_DISK is used to initialize a disk with an empty partition table.
CREATE_DISK createDisk = new CREATE_DISK();
createDisk.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
createDisk.Mbr.Signature = 1;
IntPtr createDiskBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CREATE_DISK)));
Marshal.StructureToPtr(createDisk, createDiskBuffer, false);
byte[] arr1 = new byte[Marshal.SizeOf(typeof(CREATE_DISK))];
Marshal.Copy(createDiskBuffer, arr1, 0, Marshal.SizeOf(typeof(CREATE_DISK)));
result = DeviceIoControl(handle, IOCTL_DISK_CREATE_DISK, createDiskBuffer, Marshal.SizeOf(typeof(CREATE_DISK)),
IntPtr.Zero, 0, ref size, IntPtr.Zero);
result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);
//Step 4: IOCTL_DISK_SET_DRIVE_LAYOUT_EX to repartition a disk as specified.
//Note: use IOCTL_DISK_UPDATE_PROPERTIES to synchronize system view after IOCTL_DISK_CREATE_DISK and IOCTL_DISK_SET_DRIVE_LAYOUT_EX
/* DWORD driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 4 * 25;
DRIVE_LAYOUT_INFORMATION_EX *DriveLayoutEx = (DRIVE_LAYOUT_INFORMATION_EX *) new BYTE[driveLayoutSize];*/
IntPtr driveLayoutbuffer = Marshal.AllocHGlobal(624);
DRIVE_LAYOUT_INFORMATION_EX driveLayoutEx = new DRIVE_LAYOUT_INFORMATION_EX();
int pn = 0;
driveLayoutEx.PartitionEntry = new PARTITION_INFORMATION_EX[4];
mediaType = (int)geometry.Geometry.MediaType;
Int64 bytes_per_track = (geometry.Geometry.SectorsPerTrack) * (geometry.Geometry.BytesPerSector);
driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x123;
Int64 main_part_size_in_sectors, extra_part_size_in_sectors = 0;
main_part_size_in_sectors = (geometry.DiskSize - driveLayoutEx.PartitionEntry[pn].StartingOffset) / geometry.Geometry.BytesPerSector;
if (main_part_size_in_sectors <= 0)
{
return false;
}
extra_part_size_in_sectors = (MIN_EXTRA_PART_SIZE + bytes_per_track - 1) / bytes_per_track;
main_part_size_in_sectors = ((main_part_size_in_sectors / geometry.Geometry.SectorsPerTrack) -
extra_part_size_in_sectors) * geometry.Geometry.SectorsPerTrack;
if (main_part_size_in_sectors <= 0)
{
return false;
}
driveLayoutEx.PartitionEntry[pn].PartitionLength = 50000;
driveLayoutEx.PartitionEntry[pn].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07;
driveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = true;
pn++;
// Set the optional extra partition
// Should end on a track boundary
driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x400;
driveLayoutEx.PartitionEntry[pn].PartitionLength = 26244; //TODO: Has to change
driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef;
pn++;
for (uint i = 0; i < pn; i++)
{
driveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1;
driveLayoutEx.PartitionEntry[i].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionEntry[i].RewritePartition = true;
}
driveLayoutEx.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionCount = 4; //It should be a multiple of 4
driveLayoutEx.Mbr.Mbr.Signature = createDisk.Mbr.Signature;
Marshal.StructureToPtr(driveLayoutEx, driveLayoutbuffer, false);
result = DeviceIoControl(handle, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, driveLayoutbuffer, 624, IntPtr.Zero, 0, ref size, IntPtr.Zero);
result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);
Marshal.FreeHGlobal(driveLayoutbuffer);
Marshal.FreeHGlobal(createDiskBuffer);
Marshal.FreeHGlobal(lpOutBuffer);
return true;
}
它只创建一个分区,偏移量为零,分区长度为 USB 的完整大小。
这两天我一直在尝试这个,但仍然没有解决办法。
我得到了解决方案。
这是因为:
- Marshal.AllocHGlobal 分配的内存不是零填充的。
- PARTITION_INFORMATION_GPT结构体名称成员填写的是只有8个
分配的内存字节而不是它需要 72 字节。
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct PARTITION_INFORMATION_GPT
{
/// <summary>
/// A GUID that identifies the partition type.
///
/// Each partition type that the EFI specification supports is identified by its own GUID, which is
/// published by the developer of the partition.
/// </summary>
[FieldOffset(0)]
public Guid PartitionType;
/// <summary>
/// The GUID of the partition.
/// </summary>
[FieldOffset(16)]
public Guid PartitionId;
/// <summary>
/// The Extensible Firmware Interface (EFI) attributes of the partition.
///
/// </summary>
[FieldOffset(32)]
public UInt64 Attributes;
/// <summary>
/// A wide-character string that describes the partition.
/// </summary>
[FieldOffset(40)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)]
public string Name;
}
我正在尝试使用 DeviceIOControl 在 USB 中创建多个分区。它总是只创建一个分区。
这是我的源代码
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32")]
static extern int CloseHandle(IntPtr handle);
[DllImport("kernel32")]
private static extern int DeviceIoControl
(IntPtr deviceHandle, uint ioControlCode,
IntPtr inBuffer, int inBufferSize,
IntPtr outBuffer, int outBufferSize,
ref int bytesReturned, IntPtr overlapped);
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const uint OPEN_EXISTING = 0x00000003;
public const uint FILE_ATTRIBUTE_NORMAL = 0x80;
public const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x90083;
public const int DRIVE_ACCESS_RETRIES = 10;
public const int DRIVE_ACCESS_TIMEOUT = 15000;
public const uint FSCTL_LOCK_VOLUME = 0x00090018;
static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
public const uint IOCTL_DISK_GET_DRIVE_LAYOUT_EX = 0x00070050;
public const uint IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = 0x000700A0;
public const uint IOCTL_DISK_CREATE_DISK = 0x0007C058;
public const uint IOCTL_DISK_UPDATE_PROPERTIES = 0x00070140;
public const uint IOCTL_DISK_SET_DRIVE_LAYOUT_EX = 0x0007C054;
public const int MIN_EXTRA_PART_SIZE = 1024 * 1024;
public static int mediaType = 0;
/// <summary>
/// Describes the geometry of disk devices and media.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct DISK_GEOMETRY
{
/// <summary>
/// The number of cylinders.
/// </summary>
[FieldOffset(0)]
public Int64 Cylinders;
/// <summary>
/// The type of media. For a list of values, see MEDIA_TYPE.
/// </summary>
[FieldOffset(8)]
public MEDIA_TYPE MediaType;
/// <summary>
/// The number of tracks per cylinder.
/// </summary>
[FieldOffset(12)]
public uint TracksPerCylinder;
/// <summary>
/// The number of sectors per track.
/// </summary>
[FieldOffset(16)]
public uint SectorsPerTrack;
/// <summary>
/// The number of bytes per sector.
/// </summary>
[FieldOffset(20)]
public uint BytesPerSector;
}
/// <summary>
/// Describes the extended geometry of disk devices and media.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DISK_GEOMETRY_EX
{
/// <summary>
/// A DISK_GEOMETRY structure.
/// </summary>
[FieldOffset(0)]
public DISK_GEOMETRY Geometry;
/// <summary>
/// The disk size, in bytes.
/// </summary>
[FieldOffset(24)]
public Int64 DiskSize;
/// <summary>
/// Any additional data.
/// </summary>
[FieldOffset(32)]
public Byte Data;
}
/// <summary>
/// Represents the format of a partition.
/// </summary>
public enum PARTITION_STYLE : uint
{
/// <summary>
/// Master boot record (MBR) format.
/// </summary>
PARTITION_STYLE_MBR = 0,
/// <summary>
/// GUID Partition Table (GPT) format.
/// </summary>
PARTITION_STYLE_GPT = 1,
/// <summary>
/// Partition not formatted in either of the recognized formats—MBR or GPT.
/// </summary>
PARTITION_STYLE_RAW = 2
}
/// <summary>
/// Contains partition information specific to master boot record (MBR) disks.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct PARTITION_INFORMATION_MBR
{
#region Constants
/// <summary>
/// An unused entry partition.
/// </summary>
public const byte PARTITION_ENTRY_UNUSED = 0x00;
/// <summary>
/// A FAT12 file system partition.
/// </summary>
public const byte PARTITION_FAT_12 = 0x01;
/// <summary>
/// A FAT16 file system partition.
/// </summary>
public const byte PARTITION_FAT_16 = 0x04;
/// <summary>
/// An extended partition.
/// </summary>
public const byte PARTITION_EXTENDED = 0x05;
/// <summary>
/// An IFS partition.
/// </summary>
public const byte PARTITION_IFS = 0x07;
/// <summary>
/// A FAT32 file system partition.
/// </summary>
public const byte PARTITION_FAT32 = 0x0B;
/// <summary>
/// A logical disk manager (LDM) partition.
/// </summary>
public const byte PARTITION_LDM = 0x42;
/// <summary>
/// An NTFT partition.
/// </summary>
public const byte PARTITION_NTFT = 0x80;
/// <summary>
/// A valid NTFT partition.
///
/// The high bit of a partition type code indicates that a partition is part of an NTFT mirror or striped array.
/// </summary>
public const byte PARTITION_VALID_NTFT = 0xC0;
#endregion
/// <summary>
/// The type of partition. For a list of values, see Disk Partition Types.
/// </summary>
[FieldOffset(0)]
[MarshalAs(UnmanagedType.U1)]
public byte PartitionType;
/// <summary>
/// If this member is TRUE, the partition is bootable.
/// </summary>
[FieldOffset(1)]
[MarshalAs(UnmanagedType.I1)]
public bool BootIndicator;
/// <summary>
/// If this member is TRUE, the partition is of a recognized type.
/// </summary>
[FieldOffset(2)]
[MarshalAs(UnmanagedType.I1)]
public bool RecognizedPartition;
/// <summary>
/// The number of hidden sectors in the partition.
/// </summary>
[FieldOffset(4)]
public uint HiddenSectors;
}
/// <summary>
/// Contains GUID partition table (GPT) partition information.
/// </summary>
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct PARTITION_INFORMATION_GPT
{
/// <summary>
/// A GUID that identifies the partition type.
///
/// Each partition type that the EFI specification supports is identified by its own GUID, which is
/// published by the developer of the partition.
/// </summary>
[FieldOffset(0)]
public Guid PartitionType;
/// <summary>
/// The GUID of the partition.
/// </summary>
[FieldOffset(16)]
public Guid PartitionId;
/// <summary>
/// The Extensible Firmware Interface (EFI) attributes of the partition.
///
/// </summary>
[FieldOffset(32)]
public UInt64 Attributes;
/// <summary>
/// A wide-character string that describes the partition.
/// </summary>
[FieldOffset(40)]
public string Name;
}
/// <summary>
/// Provides information about a drive's master boot record (MBR) partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_MBR
{
/// <summary>
/// The signature of the drive.
/// </summary>
[FieldOffset(0)]
public uint Signature;
}
/// <summary>
/// Contains information about a drive's GUID partition table (GPT) partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_GPT
{
/// <summary>
/// The GUID of the disk.
/// </summary>
[FieldOffset(0)]
public Guid DiskId;
/// <summary>
/// The starting byte offset of the first usable block.
/// </summary>
[FieldOffset(16)]
public Int64 StartingUsableOffset;
/// <summary>
/// The size of the usable blocks on the disk, in bytes.
/// </summary>
[FieldOffset(24)]
public Int64 UsableLength;
/// <summary>
/// The maximum number of partitions that can be defined in the usable block.
/// </summary>
[FieldOffset(32)]
public uint MaxPartitionCount;
}
/// <summary>
/// Contains information about a disk partition.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct PARTITION_INFORMATION_EX
{
/// <summary>
/// The format of the partition. For a list of values, see PARTITION_STYLE.
/// </summary>
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
/// <summary>
/// The starting offset of the partition.
/// </summary>
[FieldOffset(8)]
public Int64 StartingOffset;
/// <summary>
/// The length of the partition, in bytes.
/// </summary>
[FieldOffset(16)]
public Int64 PartitionLength;
/// <summary>
/// The number of the partition (1-based).
/// </summary>
[FieldOffset(24)]
public uint PartitionNumber;
/// <summary>
/// If this member is TRUE, the partition information has changed. When you change a partition (with
/// IOCTL_DISK_SET_DRIVE_LAYOUT), the system uses this member to determine which partitions have changed
/// and need their information rewritten.
/// </summary>
[FieldOffset(28)]
[MarshalAs(UnmanagedType.I1)]
public bool RewritePartition;
/// <summary>
/// A PARTITION_INFORMATION_MBR structure that specifies partition information specific to master boot
/// record (MBR) disks. The MBR partition format is the standard AT-style format.
/// </summary>
[FieldOffset(32)]
public PARTITION_INFORMATION_MBR Mbr;
/// <summary>
/// A PARTITION_INFORMATION_GPT structure that specifies partition information specific to GUID partition
/// table (GPT) disks. The GPT format corresponds to the EFI partition format.
/// </summary>
[FieldOffset(32)]
public PARTITION_INFORMATION_GPT Gpt;
}
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_UNION
{
[FieldOffset(0)]
public DRIVE_LAYOUT_INFORMATION_MBR Mbr;
[FieldOffset(0)]
public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
}
/// <summary>
/// Contains extended information about a drive's partitions.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct DRIVE_LAYOUT_INFORMATION_EX
{
/// <summary>
/// The style of the partitions on the drive enumerated by the PARTITION_STYLE enumeration.
/// </summary>
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
/// <summary>
/// The number of partitions on a drive.
///
/// On disks with the MBR layout, this value is always a multiple of 4. Any partitions that are unused have
/// a partition type of PARTITION_ENTRY_UNUSED.
/// </summary>
[FieldOffset(4)]
public uint PartitionCount;
/// <summary>
/// A DRIVE_LAYOUT_INFORMATION_MBR structure containing information about the master boot record type
/// partitioning on the drive.
/// </summary>
[FieldOffset(8)]
public DRIVE_LAYOUT_INFORMATION_UNION Mbr;
/// <summary>
/// A DRIVE_LAYOUT_INFORMATION_GPT structure containing information about the GUID disk partition type
/// partitioning on the drive.
/// </summary>
// [FieldOffset(8)]
//public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
/// <summary>
/// A variable-sized array of PARTITION_INFORMATION_EX structures, one structure for each partition on the
/// drive.
/// </summary>
[FieldOffset(48)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 4)]
public PARTITION_INFORMATION_EX[] PartitionEntry;
}
[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK_MBR
{
[FieldOffset(0)]
public uint Signature;
}
[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK_GPT
{
[FieldOffset(0)]
public Guid DiskId;
[FieldOffset(16)]
public uint MaxPartitionCount;
}
[StructLayout(LayoutKind.Explicit)]
private struct CREATE_DISK
{
[FieldOffset(0)]
public PARTITION_STYLE PartitionStyle;
[FieldOffset(4)]
public CREATE_DISK_MBR Mbr;
[FieldOffset(4)]
public CREATE_DISK_GPT Gpt;
}
static IntPtr GetHandle(int driveIndex)
{
IntPtr handle;
//bool locked = false;
Program p = new Program();
string physicalName = p.GetPhysicalName(driveIndex);
Debug.WriteLine(physicalName);
handle = CreateFile(physicalName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (handle == INVALID_HANDLE_VALUE)
{
Debug.WriteLine(Marshal.GetLastWin32Error());
return IntPtr.Zero;
}
return handle;
}
//Returns true if drive Index is successfully created
//Returns false if not created successfully
static bool CreatePartition(int driveIndex)
{
IntPtr handle = GetHandle(driveIndex);
if (handle == INVALID_HANDLE_VALUE)
{
return false;
}
//Step 2: IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to get the physical disk's geometry ( we need some information in it to fill partition data)
//The number of surfaces (or heads, which is the same thing), cylinders, and sectors vary a lot; the specification of the number of each is called the geometry of a hard disk.
//The geometry is usually stored in a special, battery-powered memory location called the CMOS RAM , from where the operating system can fetch it during bootup or driver initialization.
int size = 0;
DISK_GEOMETRY_EX geometry = new DISK_GEOMETRY_EX();
IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)));
Marshal.StructureToPtr(geometry, lpOutBuffer, false);
int result = DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, IntPtr.Zero, 0, lpOutBuffer,
Marshal.SizeOf(typeof(DISK_GEOMETRY_EX)),
ref size, IntPtr.Zero);
geometry = (DISK_GEOMETRY_EX)Marshal.PtrToStructure(lpOutBuffer, typeof(DISK_GEOMETRY_EX));
//Step 3: IOCTL_DISK_CREATE_DISK is used to initialize a disk with an empty partition table.
CREATE_DISK createDisk = new CREATE_DISK();
createDisk.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
createDisk.Mbr.Signature = 1;
IntPtr createDiskBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CREATE_DISK)));
Marshal.StructureToPtr(createDisk, createDiskBuffer, false);
byte[] arr1 = new byte[Marshal.SizeOf(typeof(CREATE_DISK))];
Marshal.Copy(createDiskBuffer, arr1, 0, Marshal.SizeOf(typeof(CREATE_DISK)));
result = DeviceIoControl(handle, IOCTL_DISK_CREATE_DISK, createDiskBuffer, Marshal.SizeOf(typeof(CREATE_DISK)),
IntPtr.Zero, 0, ref size, IntPtr.Zero);
result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);
//Step 4: IOCTL_DISK_SET_DRIVE_LAYOUT_EX to repartition a disk as specified.
//Note: use IOCTL_DISK_UPDATE_PROPERTIES to synchronize system view after IOCTL_DISK_CREATE_DISK and IOCTL_DISK_SET_DRIVE_LAYOUT_EX
/* DWORD driveLayoutSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 4 * 25;
DRIVE_LAYOUT_INFORMATION_EX *DriveLayoutEx = (DRIVE_LAYOUT_INFORMATION_EX *) new BYTE[driveLayoutSize];*/
IntPtr driveLayoutbuffer = Marshal.AllocHGlobal(624);
DRIVE_LAYOUT_INFORMATION_EX driveLayoutEx = new DRIVE_LAYOUT_INFORMATION_EX();
int pn = 0;
driveLayoutEx.PartitionEntry = new PARTITION_INFORMATION_EX[4];
mediaType = (int)geometry.Geometry.MediaType;
Int64 bytes_per_track = (geometry.Geometry.SectorsPerTrack) * (geometry.Geometry.BytesPerSector);
driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x123;
Int64 main_part_size_in_sectors, extra_part_size_in_sectors = 0;
main_part_size_in_sectors = (geometry.DiskSize - driveLayoutEx.PartitionEntry[pn].StartingOffset) / geometry.Geometry.BytesPerSector;
if (main_part_size_in_sectors <= 0)
{
return false;
}
extra_part_size_in_sectors = (MIN_EXTRA_PART_SIZE + bytes_per_track - 1) / bytes_per_track;
main_part_size_in_sectors = ((main_part_size_in_sectors / geometry.Geometry.SectorsPerTrack) -
extra_part_size_in_sectors) * geometry.Geometry.SectorsPerTrack;
if (main_part_size_in_sectors <= 0)
{
return false;
}
driveLayoutEx.PartitionEntry[pn].PartitionLength = 50000;
driveLayoutEx.PartitionEntry[pn].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0x07;
driveLayoutEx.PartitionEntry[pn].Mbr.BootIndicator = true;
pn++;
// Set the optional extra partition
// Should end on a track boundary
driveLayoutEx.PartitionEntry[pn].StartingOffset = 0x400;
driveLayoutEx.PartitionEntry[pn].PartitionLength = 26244; //TODO: Has to change
driveLayoutEx.PartitionEntry[pn].Mbr.PartitionType = 0xef;
pn++;
for (uint i = 0; i < pn; i++)
{
driveLayoutEx.PartitionEntry[i].PartitionNumber = i + 1;
driveLayoutEx.PartitionEntry[i].PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionEntry[i].RewritePartition = true;
}
driveLayoutEx.PartitionStyle = PARTITION_STYLE.PARTITION_STYLE_MBR;
driveLayoutEx.PartitionCount = 4; //It should be a multiple of 4
driveLayoutEx.Mbr.Mbr.Signature = createDisk.Mbr.Signature;
Marshal.StructureToPtr(driveLayoutEx, driveLayoutbuffer, false);
result = DeviceIoControl(handle, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, driveLayoutbuffer, 624, IntPtr.Zero, 0, ref size, IntPtr.Zero);
result = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, ref size, IntPtr.Zero);
Marshal.FreeHGlobal(driveLayoutbuffer);
Marshal.FreeHGlobal(createDiskBuffer);
Marshal.FreeHGlobal(lpOutBuffer);
return true;
}
它只创建一个分区,偏移量为零,分区长度为 USB 的完整大小。
这两天我一直在尝试这个,但仍然没有解决办法。
我得到了解决方案。
这是因为:
- Marshal.AllocHGlobal 分配的内存不是零填充的。
- PARTITION_INFORMATION_GPT结构体名称成员填写的是只有8个 分配的内存字节而不是它需要 72 字节。
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct PARTITION_INFORMATION_GPT
{
/// <summary>
/// A GUID that identifies the partition type.
///
/// Each partition type that the EFI specification supports is identified by its own GUID, which is
/// published by the developer of the partition.
/// </summary>
[FieldOffset(0)]
public Guid PartitionType;
/// <summary>
/// The GUID of the partition.
/// </summary>
[FieldOffset(16)]
public Guid PartitionId;
/// <summary>
/// The Extensible Firmware Interface (EFI) attributes of the partition.
///
/// </summary>
[FieldOffset(32)]
public UInt64 Attributes;
/// <summary>
/// A wide-character string that describes the partition.
/// </summary>
[FieldOffset(40)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)]
public string Name;
}