This is a code sample to give you an idea how to incorporate a Split function to your solution in C#. The Split functionality is one of the advanced partitioning tasks.

The Split functionality is key to keeping your data storage healthy. Keeping all the data on a single volume negatively affects the system: unorganized storage leads to inevitable excessive fragmentation. As a result, it is difficult to search and access the files, and it hinders the read/write operations. Finally, it can cause serious issues in case of a system malfunction. 

By incorporating the Split functionality into your customized solution you can separate the operating system and data or different types of data by splitting one partition into two different partitions of the same type and file system.

Limitations to consider before proceeding with the Split functionality:

  • Partition’s type is not supported (a non-standard primary or logical partition);
  • The selected partition is located on a dynamic disk;
  • There are 4 primary partitions on a Basic MBR disk;
  • There are 3 primary partitions on a Hybrid (Retained) GPT+MBR disk;
  • There’s not enough free space on the selected partition (free up to 50 MB);
  • The selected partition has an unsupported file system.

Due to internal restrictions, hdmengine cannot work on multiple processes at the same time, so it locks the volume during the process to restrict access from another process. For example, if an additional process is initialized (for example, resize a partition) when the hdmengine is busy with a different process (for example, creating a partition), the engine will not be able to lock the additional process (resize a partition) and will execute it in the Bluescreen mode.

Before you read the sample code:

  • add hdmengine_hdmclientinteroplib.dll library to your project.

There are three basic steps in the tutorial: hdmengine initialization, executing the Split function, and closing the hdmengine and API process.

Define the Paragon.DiskMgmt namespace to bring hdmengine interfaces into global scope, and System – to bring standard interfaces.

namespace ExampleHdm
{
    using Paragon.DiskMgmt;
    using System;

Declare the main class of the application.

public class PartitionSplitApp
    {

Entry point to application. Launch the main function with the administrator privileges. Be aware that GPT disk has hidden partitions, that are not shown in Disk Management UI. There are three parameters that you introduce – disk number, partition number, and the size of the parent size in GB. 

public static int Main(String[] cliArgs)
{
    if(cliArgs.Length != 3)
    {
        Console.WriteLine("Usage: partition_split.exe <disk number> <partition number> <parent partition size GB>");
        return -1;
    }
    int diskNumber = Convert.ToInt32(cliArgs[0]);
    int partitionNumber = Convert.ToInt32(cliArgs[1]);
    int parentSizeGB = Convert.ToInt32(cliArgs[2]);

Specify the Hdmengine logs directory.

String logsDirectory = "C:\\Windows\\Temp\\hdmengine_example";

Specify the Hdmengine bluescreen (kernel) mode logs directory.

String bsLogsDirectory = "C:\\Windows\\Temp\\hdmengine_example";
String customCopyright = "My custom copyright";

Initialize progress callbacks interface.

ProgressOutput progress = new ProgressOutput();

To be able to use API methods – create Hdmengine in-process interface.

IHdmengineApi hdmInProcess = new HdmengineInProcess(progress, logsDirectory);

Initialize the hdmengine.

String version;
uint status = hdmInProcess.Init(HdmclientInitFlags.EnableRunMultiple, out version);
if (status == (uint)DiskMgmtOperationStatus.SERR_SUCCESS)
    Console.WriteLine("Hdmclient initialized, version = {0}", version);
else
{
    Console.WriteLine("Hdmclient initialized, version = {0}, status = {1}", version, status);
    return -2;
}

Create the layout.ini file to change the parameters of the hdmengine. This file also contains the path to the bluescreen logs. Remember to place the bluescreen logs folder on a different volume to the one you are performing operations on.

status = hdmInProcess.ChangeLayoutSettings(true, bsLogsDirectory, customCopyright);
if (status != (uint)DiskMgmtOperationStatus.SERR_SUCCESS)
{
    Console.WriteLine("Hdmengine can'not change layout {0}", status);
    return -2;
}

To perform operations on a specific disk, you need to get the list of disks and partitions available in the system.

DiskInfoExtracted[] disks;
status = hdmInProcess.GetDisksInfo(out disks);
if (status != (uint)DiskMgmtOperationStatus.SERR_SUCCESS)
{
    Console.WriteLine("Failed to get disks info {0}", status);
    return -2;
}
if (disks == null || disks.Length == 0)
{
    status = (uint)DiskMgmtOperationStatus.SERR_DISK_NOT_FOUND;
    Console.WriteLine("Disk not found {0}", status);
    return -2;
}

This is the main section to enter the code for the Split functionality (see the detailed breakdown for the code below). You can add more than one functionality in queue of operations. Add masks for files and folders in parent partition.

Char partLetter = hdmInProcess.GetNextAvailableDriveLetter()
String[] excluded = new string[3];
excluded[0] = "/Excluded data dir0";
excluded[1] = "/Excluded data dir1";
excluded[2] = "*.exe";

Add masks for files and folders in child partition.

String[] splitted = new string[2];
splitted[0] = "/Splitted data dir0";
splitted[1] = "/Splitted data dir1";
status = PartitionSplit(hdmInProcess, disks, diskNumber, partitionNumber, partLetter,
    "New partition", (UInt64)parentSizeGB * 0x40000000, excluded, splitted);
if (status != (uint)DiskMgmtOperationStatus.SERR_SUCCESS)
    return -2;

Apply any pending operations or the queue of operations. This process also installs the bluescreen reboot, if required. Bluescreen jobs that need a restart, can be checked as hdmInProcess.IsNeedReboot().

status = hdmInProcess.Apply();
if(status != (uint)DiskMgmtOperationStatus.SERR_SUCCESS)
{
    Console.WriteLine("Hdmengine can'not apply operation {0}", status);
}

Close Hdmengine, free all resources. Reboot system, if there were operations, that required reboot. If instead of the hdmInProcess.IsNeedReboot value, a False is defined, the user needs to reboot the application explicitly.

    hdmInProcess.Close(hdmInProcess.IsNeedReboot(), false);
 
    return 0;
}

Split functionality setup – select the partition or volume from the list of the available disks and partitions.

static uint PartitionSplit(IHdmengineApi hdmApi, DiskInfoExtracted[] disks, int diskNumber, int partitionNumber,
            Char childLetter, String childLabel, UInt64 parentSizeBytes,
            String[] parentMask, String[] childMask)
        {
            uint err = (uint)DiskMgmtOperationStatus.SERR_SUCCESS;
            bool foundDisk = false;
            bool foundPartition = false;
            PartitionInfoExtracted partInfo = new PartitionInfoExtracted();
            DiskInfoExtracted diskInfo = new DiskInfoExtracted();
            if (diskNumber >= disks.Length)
            {
                err = (uint)DiskMgmtOperationStatus.SERR_DISK_NOT_FOUND;
                Console.WriteLine($"Disk number is out of range (maximum number is {disks.Length}, diskNumber = {diskNumber}), {err}");
                return err;
            }
            else
            {
                diskInfo = disks[diskNumber];
                foundDisk = true;
                if (diskInfo.partitions == null || (partitionNumber >= diskInfo.partitions.Length))
                {
                    err = (uint)DiskMgmtOperationStatus.SERR_PART_NOT_FOUND;
                    Console.WriteLine($"Partition number is out of range (maximum number is {diskInfo.partitions.Length}, partitionNumber is {partitionNumber}), {err}");
                    return err;
                }
                else
                {
                    partInfo = diskInfo.partitions[partitionNumber];
                    foundPartition = true;
                }
            }

Verify that the Split function is available and can be performed on the selected partition.

if ((partInfo.ActFlags & PartitionActFlags.CanSplitFs) == 0)
            {
                err = (uint)DiskMgmtOperationStatus.SERR_INCOMPATIBLEOP;
 
                Console.WriteLine($"Partition split is not enabled for selected partition ... Failed, {err}\n");
                return err;
            }

Call to engine method. Add the split filesystem operation to queue. The actual job will be done on engine.Apply().

            err = hdmApi.PartitionSplitFs(partInfo.DiskNumber,
                                              partInfo.PartitionNumber, parentSizeBytes, childLetter,
                                              childLabel, parentMask, childMask);
 
            if (err == (uint)DiskMgmtOperationStatus.SERR_SUCCESS)
                Console.WriteLine($"Split selected partition file system queue ... Success\n");
            else
            {
                Console.WriteLine($"Split selected partition file system queue ... Failed, {err}\n");
                return err;
            }
 
            return err;
        }
 
    }
}

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.