Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Determining all bootable partitions

2.81/5 (6 votes)
28 Jun 2018CPOL2 min read 20.9K   328  
Determining all bootable partitions using PInvoke

Image 1

Introduction

In this article, I will show how to determine boot partition of Windows OS with C# by using Win API (pinvoke).

Background

Windows OS has different types of partition on the disk. And there must be a system partition generally (C:\), other than this, there may be user partitions such as D: \, E:\. There may also be another type of partition such as Boot partition which is what constitutes the subject of our writing. Sometimes, developers want to identify boot partition in order not to damage the system because this partition is very important for boot operation of Windows OS.

Thanks to Richard, I realized boot indicator property is not valid on GPT disk. So I updated source code and article as compatible with GPT disk.

Using the Code

I used C# (Pinvoke). I have to define classic MS definitions which are structs, constants, etc. So I won't mention these definitions below.

The Native class contains definitions of structs, constants of Win API.

The NativeApi the class contains functions of Win API like,CreateFileDeviceIoControl  etc.

Firstly; We need to obtain handles of all hard drives which are mounted on the system. So we enumerate these drives. And then we can get partition list for all physical drives. Then we can enumerate all partitions.

This is full of body code:

C#
  public const uint MAX_NUMBER_OF_DRIVES = 64;

  for (uint i = 0; i < MAX_NUMBER_OF_DRIVES; i++)
  {
     // try to open the current physical drive
     string volume = string.Format("\\\\.\\PhysicalDrive{0}", i); 
     SafeFileHandle hndl = CreateFile(volume, Native.GENERIC_READ | Native.GENERIC_WRITE,
                                                Native.FILE_SHARE_READ | Native.FILE_SHARE_WRITE,
                                                IntPtr.Zero,
                                                Native.OPEN_EXISTING,
                                                Native.FILE_ATTRIBUTE_READONLY,
                                                IntPtr.Zero);
     //We have got handle now.

     //Some necessary variables definitions
     IntPtr driveLayoutPtr = IntPtr.Zero;
     int DRIVE_LAYOUT_BUFFER_SIZE = 1024;
     int error;
     uint dummy = 0;
                
     do
     {
       error = 0;
       driveLayoutPtr = Marshal.AllocHGlobal(DRIVE_LAYOUT_BUFFER_SIZE);
       if (DeviceIoControl
          (hndl, Native.IOCTL_DISK_GET_DRIVE_LAYOUT_EX, IntPtr.Zero, 0, driveLayoutPtr, 
          (uint)DRIVE_LAYOUT_BUFFER_SIZE, ref dummy, IntPtr.Zero))
          {
            // I/O-control has been invoked successfully, 
            //convert to DRIVE_LAYOUT_INFORMATION_EX
             DRIVE_LAYOUT_INFORMATION_EX driveLayout = (DRIVE_LAYOUT_INFORMATION_EX)
             Marshal.PtrToStructure(driveLayoutPtr, typeof(DRIVE_LAYOUT_INFORMATION_EX));
             //Now i have got partition
             for (uint p = 0; p < driveLayout.PartitionCount; p++)
             {
                //Enumerate partition by using pointer arithmetic
                IntPtr ptr = new IntPtr(driveLayoutPtr.ToInt64() + 
                Marshal.OffsetOf(typeof(DRIVE_LAYOUT_INFORMATION_EX), "PartitionEntry").ToInt64()
                + (p * Marshal.SizeOf(typeof(PARTITION_INFORMATION_EX))));

                PARTITION_INFORMATION_EX partInfo = (PARTITION_INFORMATION_EX)
                Marshal.PtrToStructure(ptr, typeof(PARTITION_INFORMATION_EX));
                //Check partition is recognized or not
               if (partInfo.PartitionStyle != PARTITION_STYLE.PARTITION_STYLE_GPT)
               {
                 if ((partInfo.PartitionStyle != PARTITION_STYLE.PARTITION_STYLE_MBR) || 
                     (partInfo.Mbr.RecognizedPartition))
                 {
                   if (partInfo.Mbr.BootIndicator == true)
                   {
                    Console.WriteLine("Drive No: " + i + " Partition Number :" + 
                                       partInfo.PartitionNumber + " is boot partition");

                   }

                 }
               }
               else if (partInfo.PartitionStyle == PARTITION_STYLE.PARTITION_STYLE_GPT) {
                 //e3c9e316-0b5c-4db8-817d-f92df00215ae guid is defined from MS here:
                 //https://msdn.microsoft.com/en-us/library/windows/desktop/aa365449(v=vs.85).aspx
                 if (partInfo.Gpt.PartitionType== new Guid("e3c9e316-0b5c-4db8-817d-f92df00215ae"))
                 {
                 Console.WriteLine("Drive No: " + i + " Partition Number :" 
                                  + partInfo.PartitionNumber + " is boot partition");


                 }
              }
             }
            }
       else
       {
          error = Marshal.GetLastWin32Error();
          DRIVE_LAYOUT_BUFFER_SIZE *= 2;                    
        }
       Marshal.FreeHGlobal(driveLayoutPtr);
       driveLayoutPtr = IntPtr.Zero;
  } while (error == Native.ERROR_INSUFFICIENT_BUFFER);
}

As seen above, we get all partitions and check each one is BootIndicator or not.

The important thing is at first, filling DRIVE_LAYOUT_INFORMATION_EX structure by using the DeviceIoControl function. And then there is pointer arithmetic to extract buffer. Finally, We need to convert byte buffer to the PARTITION_INFORMATION_EX structure. That's all!

Don't forget that boot partition doesn't have to be separated on all Windows installed machines. Consider that boot OS files may be on OS installed partition which is generally " Local Disk (C:) ".

Finally, if you want to test sample application, the application requires administrator credentials to run correctly.

 

Points of Interest

I wrote this article for developers which are interested in Win API programming. And I think low-level programming is very hard and interesting. So I like learning new things when I develop a program. I think if you are interested in learning fundamentals of something, then this topic will help you to identify boot partition programmatically. I hope you enjoyed reading this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)