2

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);
}
David Ryan
  • 25
  • 1
  • 5

2 Answers2

4

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
};
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Thanks for the reply MSalters. Could you explain how to implement the solution you provided? Apologies for the annoyance, but I am relatively new to c++. – David Ryan Mar 15 '13 at 15:42
  • @DavidRyan: The Windows structure isn't clean C++, but you have to consider that Windows is in theory language-neutral. Its API doesn't always map nicely to C++. In this case, you need an object with more than 1 `PartitionEntry` member. I use C++ derivation to add extra members, and rely on the compiler to get tha layout right. Now replace `DRIVE_LAYOUT_INFOMARTION_EX` by `ExtraEntries` in your program. Since it's a derived type, when you pass an `ExtraEntries*` to the Windows API, the compiler will automatically insert the cast-to-base to give you the needed `DRIVE_LAYOUT_INFORMATION_EX*`. – MSalters Mar 18 '13 at 10:53
1

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

Inspired of an answer from 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
}
faceslog
  • 11
  • 3