Question

I am currently trying to write a C++ program to automate retrieving information about the partitions of a sample hard-drive image, the information in question being the number of partitions on the disk and for each partition its start sector, size and and file system type.

I'm pretty sure at this point the best way to achieve this is through MSDN functions, microsofts inbuilt commands. I am trying to use the "IOCTL_DISK_GET_DRIVE_LAYOUT_EX" function, but according to my get error call my function is incorrect. When I debug the program is appears that the bool value is also unchanged after the "IOCTL_DISK_GET_DRIVE_LAYOUT_EX" call, meaning it is not returning the bResult value.

I am using Microsoft Visual C++ Express Edition. If people could take a look at my code and tell me what they think I did wrong it would be much appreciated.

#define UNICODE 1
#define _UNICODE 1

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

#define wszDrive L"\\\\.\\PhysicalDrive6"

BOOL GetDriveParition(LPWSTR wszPath, DRIVE_LAYOUT_INFORMATION_EX *pdg)
{

  HANDLE hDevice = INVALID_HANDLE_VALUE;  // handle to the drive to be examined 
  BOOL bResult   = FALSE;                 // results flag
  DWORD junk     = 0;                     // discard results


  hDevice = CreateFileW(wszPath,          // drive to open
                    0,                // no access to the drive
                    FILE_SHARE_READ | // share mode
                    FILE_SHARE_WRITE, 
                    NULL,             // default security attributes
                    OPEN_EXISTING,    // disposition
                    0,                // file attributes
                    NULL);            // do not copy file attributes

  if (hDevice == INVALID_HANDLE_VALUE)    // cannot open the drive
  {
return (FALSE);
  }

bResult =  DeviceIoControl( 
                  hDevice,                        // handle to device
                  IOCTL_DISK_GET_DRIVE_LAYOUT_EX, // dwIoControlCode
                  NULL,                           // lpInBuffer
                  0,                              // nInBufferSize
                  pdg,                            // lpOutBuffer
                  sizeof(*pdg),                   // nOutBufferSize
                  &junk,                          // lpBytesReturned
                  NULL);                          // lpOverlapped

CloseHandle(hDevice);

return (bResult);


}

int wmain(int argc, wchar_t *argv[])
{
DRIVE_LAYOUT_INFORMATION_EX pdg; // disk drive partition structure
  BOOL bResult = FALSE;      // generic results flag

  bResult = GetDriveParition (wszDrive, &pdg);

  if (bResult) 
  {
    wprintf(L"Drive path            = %ws\n",   wszDrive);
    wprintf(L"Partition Style       = %I64d\n", pdg.PartitionStyle);
    wprintf(L"Partition Count       = %ld\n",   pdg.PartitionCount);

    system("Pause");
  } 
  else 
  {
    wprintf (L"GetDrivePartition failed. Error %ld.\n", GetLastError ());
    system("Pause");
  }

  return ((int)bResult);
}
Was it helpful?

Solution

DRIVE_LAYOUT_INFORMATION_EX is a weird structure. It's defined as

struct {
  DWORD                    PartitionStyle;
  DWORD                    PartitionCount;
  union {
    DRIVE_LAYOUT_INFORMATION_MBR Mbr;
    DRIVE_LAYOUT_INFORMATION_GPT Gpt;
  };
  PARTITION_INFORMATION_EX PartitionEntry[ 1 ];
}

but usually PartitionEntry is treated as a much larger array, with PartitionCount entries. This is similar to the C99 VLA mechanism. Since you'va allocated just sizeof(*pdg) bytes, there's no room for even a second PartitionEntry.

C++ hack:

struct ExtraEntries : DRIVE_LAYOUT_INFORMATION_EX
{
   PARTITION_INFORMATION_EX PartitionEntry[ 9 ]; // Or some other reasonable value
};

OTHER TIPS

Even if this post is a bit old, I found another way to get a fully populated PartitionEntry without creating a tricky struct:

According to this post: How to call DeviceIoControl to retrieve the amount of memory it needs?

DRIVE_LAYOUT_INFORMATION_EX dli;
DWORD bytesReturned = 0;

if (!DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, (void*)&dli, sizeof(dli), &bytesReturned, NULL))
{
    // Check last error if not ERROR_INSUFFICIENT_BUFFER then return
    int nError = GetLastError();
    if (nError != ERROR_INSUFFICIENT_BUFFER)
    {
        // std::cout << "DeviceIoControl() Failed: " << nError << std::endl;
        CloseHandle(hDevice);
        return false;
    }

    // Allocate enough buffer space based of the value of Partition Count:
    size_t size = offsetof(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[dli.PartitionCount]);
    std::vector<BYTE> buffer(size);

    if (!DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, (void*)buffer.data(), size, &bytesReturned, NULL))
    {
        nError = GetLastError();
        // std::cout << "DeviceIoControl() Failed: " << nError << std::endl;
        CloseHandle(hDevice);
        return false;
    }

    const DRIVE_LAYOUT_INFORMATION_EX& result = *reinterpret_cast<const DRIVE_LAYOUT_INFORMATION_EX*>(buffer.data());
    // Here all parition entry are populated ...
    // TO DO... Do your stuff with result
    
}
else
{
    // Call succeeded; dli is populated with a signle partition entry
    // TO DO... Do your stuff with dli
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top