使用 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 的完整大小。

这两天我一直在尝试这个,但仍然没有解决办法。

我得到了解决方案。

这是因为:

  1. Marshal.AllocHGlobal 分配的内存不是零填充的。
  2. 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;
}