简体   繁体   中英

Error partitioning and formatting USB flash drive in C++

I'm stuck attempting to re-partition and format a USB flash drive using C++, any help would be great!

The goal is to re-partition any arbitrary flash drive with a single partition taking the entire space and formatted FAT32 (later options NTFS and EXFAT). This will be done in batch, hopefully with 50+ devices at once, so drive letter access is not an option. I'm able to create a partition, but when I try IOCTL_DISK_SET_PARTITION_INFO_EX to set the format type, it is failing with 0x32, ERROR_NOT_SUPPORTED. But it's not clear what exactly is not supported. I can manually partition the device using utilities such as diskpart, so I know the partition and file system types are supported by the device. Can anyone help? My full source code is below, it's failing on the DeviceIoControl() call with IOCTL_DISK_SET_PARTITION_INFO_EX.

#include "stdafx.h"
#include <random>
#include <Windows.h>
#include <atlstr.h>
#include <iostream>
#include <assert.h>


using namespace std;

#define THROW_CSTRING(a, b) { CString csE; csE.Format(a, b); throw csE; }
#define RANDOM_DWORD {DWORD(rand()) | DWORD(rand() << 8) | DWORD(rand() << 16) | DWORD(rand() << 24)}


int main()
{
  DRIVE_LAYOUT_INFORMATION_EX* pdg = NULL;
  HANDLE hDevice = INVALID_HANDLE_VALUE;

  try
  {

    hDevice = CreateFile(L"\\\\.\\PhysicalDrive2",
                          GENERIC_READ | GENERIC_WRITE, 
                          0,              // Only we can access 
                          NULL,           // Default security
                          OPEN_EXISTING,  // For hardware, open existing 
                          0,              // File attributes
                          NULL);          //Do not copy attributes 
    if (hDevice == INVALID_HANDLE_VALUE)
    {
      THROW_CSTRING(L"ERROR: CreateFile() failed: 0x%x", GetLastError());
    }

    CREATE_DISK dsk;
    memset(&dsk, 0, sizeof(dsk));
    CREATE_DISK_MBR dskmbr = { 0 };
    dskmbr.Signature = 1;
    dsk.PartitionStyle = PARTITION_STYLE_MBR;
    dsk.Mbr = dskmbr;

    // DRIVE_LAYOUT_INFORMAITON_EX has an array of partition info at the end, need enough for 4 partitions minimum
    int iDriveLayoutBytesRequired = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 3;
    pdg = (DRIVE_LAYOUT_INFORMATION_EX*)new BYTE[iDriveLayoutBytesRequired];
    memset(pdg, 0, iDriveLayoutBytesRequired);

    DRIVE_LAYOUT_INFORMATION_MBR mbrlayout = { 0 };
    mbrlayout.Signature = RANDOM_DWORD;
    pdg->PartitionStyle = PARTITION_STYLE_MBR;
    pdg->Mbr = mbrlayout;
    pdg->PartitionCount = 1;

    DWORD dwBytesReturned = 0;


    if (!DeviceIoControl(hDevice, IOCTL_DISK_CREATE_DISK, &dsk, sizeof(dsk), NULL, 0, &dwBytesReturned, NULL))
    {
      THROW_CSTRING(L"ERROR: IOCTL_DISK_CREATE_DISK failed: 0x%x", GetLastError());
    }


    // Get the drive dimensions, then use that info to create a new partition 

    // Drive length
    GET_LENGTH_INFORMATION sLenInfo = { 0 };
    if (!DeviceIoControl(hDevice, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &sLenInfo, sizeof(sLenInfo), &dwBytesReturned, NULL))
    {
      THROW_CSTRING(L"ERROR: IOCTL_DISK_GET_LENGTH_INFO failed: 0x%x", GetLastError());
    }
    assert(sizeof(sLenInfo.Length.QuadPart) == sizeof(__int64));
    __int64 iDiskLengthBytes = sLenInfo.Length.QuadPart;


    pdg->PartitionStyle = PARTITION_STYLE_MBR;
    pdg->PartitionCount = 4;
    pdg->Mbr.Signature = 1;

    pdg->PartitionEntry[0].PartitionStyle = PARTITION_STYLE_MBR;
    pdg->PartitionEntry[0].StartingOffset.QuadPart = 0;
    pdg->PartitionEntry[0].PartitionLength.QuadPart = iDiskLengthBytes;
    pdg->PartitionEntry[0].PartitionNumber = 1;
    pdg->PartitionEntry[0].RewritePartition = TRUE;

    //pdg->PartitionEntry[0].Mbr.PartitionType = PARTITION_IFS; // NTFS
    pdg->PartitionEntry[0].Mbr.PartitionType = PARTITION_FAT32;
    pdg->PartitionEntry[0].Mbr.BootIndicator = TRUE;
    pdg->PartitionEntry[0].Mbr.RecognizedPartition = 1;
    pdg->PartitionEntry[0].Mbr.HiddenSectors = 0;


    // Partition device
    if (!DeviceIoControl(hDevice, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, pdg, iDriveLayoutBytesRequired, NULL, 0, &dwBytesReturned, NULL))
    {
      THROW_CSTRING(L"ERROR: IOCTL_DISK_SEt_DRIVE_LAYOUT_EX failed: 0x%x", GetLastError());
    }

    // Tell the driver to flush its cache
    if (!DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwBytesReturned, NULL))
    {
      THROW_CSTRING(L"ERROR: IOCTL_DISK_UPDATE_PROPERTIES failed: 0x%x", GetLastError());
    }

    SET_PARTITION_INFORMATION_EX dskinfo;
    memset(&dskinfo, 0, sizeof(dskinfo));
    dskinfo.PartitionStyle = PARTITION_STYLE_MBR;
    dskinfo.Mbr.PartitionType = PARTITION_FAT32;

    if (!DeviceIoControl(hDevice, IOCTL_DISK_SET_PARTITION_INFO_EX, &dskinfo, sizeof(dskinfo), NULL, 0, &dwBytesReturned, NULL))
    {
      THROW_CSTRING(L"ERROR: IOCTL_DISK_SET_PARTITION_INFO_EX failed: 0x%x", GetLastError());
    }

  }

  catch (CString csErr)
  {
    // Error lookup: https://msdn.microsoft.com/en-us/library/w indows/desktop/ms681382(v=vs.85).aspx
    // 0x7a - ERROR_INSUFFICIENT_BUFFER
    // 0x57 - ERROR_INVALID_PARAMETER
    // 0x32 - ERROR_NOT_SUPPORTED
    // 0x18 - ERROR_BAD_LENGTH
    // 0x05 - ERROR_ACCESS_DENIED
    wcout << csErr.GetString();
  }

  CloseHandle(hDevice);
  delete pdg;
  return 0;
}

I have a solution, but it's a bit convoluted. I'm using DeviceIoControl() as above to partition the disk. Then I use VDS and the IID_IVdsVolumeMF interface to create the file system, but getting there is a bit of work. The goal is to partition and format all flash drives (USB sticks) on the system. VDS will perform the format via the IID_IVdsVolumeMF interface, BUT it will not tell you (at least I haven't figured out how) which devices are removable. But WMI will tell you which devices are removable, but doesn't have a format function. So...

First use WMI to get a list of all removable volume paths on the system, for example:

CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc)
pLoc->ConnectServer(CComBSTR(L"ROOT\\CIMV2"), nullptr, nullptr, nullptr, 0, nullptr, nullptr, pWbemSvc)
CoSetProxyBlanket(
                                      *pWbemSvc,                        // Indicates the proxy to set
                                      RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
                                      RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
                                      NULL,                        // Server principal name 
                                      RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
                                      RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
                                      NULL,                        // client identity
                                      EOAC_NONE                    // proxy capabilities 

pWbemSvc->ExecQuery(CComBSTR(L"WQL"), CComBSTR(L"SELECT * FROM Win32_Volume WHERE DriveType=2"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator)

Gives you paths such as:

 L"\\\\?\\Volume{3899cb7b-7c3f-11e6-bf82-005056c00008}\\"

Then use VDS to get a list of all VDS volumes on the machine. Basically you load VDS, then get all software providers. This source is missing parts for brevity, but I think I left enough to explain what's happening:

pSvc->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &pEnumProviders)

Now iterate through the list of providers getting the packs from each provider:

pEnumProviders->Next(1, &pUnk, &cFetched)
pProv = pUnk;
pProv->QueryPacks(&pEnumpacks)
vPacks.push_back(pEnumpacks);

Now iterate through the packs and get all of the volumes in each pack:

iterator iPacks = vPacks.begin();
(*iPacks)->Next(1, &pUnk, &cFetched)
pPack = pUnk;
pPack->QueryVolumes(&pEnumvolumes)
pvpEnumvolumes->push_back(pEnumvolumes)

Now you have a list of paths to removable devices, and you have a list of all volumes on the system. Time to compare them and figure out which volumes are removable .

iVolEnum = pvpEnumOfVDSVolumes->begin()
(*iVolEnum)->Next(1, &pUnk, &cFetched)
pVMF3 = pUnk;
CComHeapPtr<LPWSTR> pVDSVolumePaths;
pVMF3->QueryVolumeGuidPathnames(&pVDSVolumePaths, &nPaths)
iterator iWMIVolPath = pvWMIRemovableVols->begin();
loop..
if (wcscmp(iWMIVolPath->data(), pVDSVolumePaths[i]) == 0)
{  // VDS Vol is removable! }

Now use this VDS volume object to format the volume:

foreach( vol in vRemovableVDSVols )
{
CComQIPtr<IVdsVolume> pVolume = *(vol);
IVdsVolumeMF *pVolumeMF;
pVolume->QueryInterface(IID_IVdsVolumeMF, (void **)&pVolumeMF);
pVolumeMF->Format(  VDS_FST_FAT32, 
                              L"MyFob", 
                              512, // alloc size
                              true, // force
                              false, // quick
                              false, // compression
                              &pAsync); // async
}

And presto your USB stick is formatted! Whew.. but it seems to be working.

Could Microsoft really not have made this any easier?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM