简体   繁体   English

在C ++中对USB闪存驱动器进行分区和格式化时出错

[英]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! 我在尝试使用C ++重新分区和格式化USB闪存驱动器时遇到问题,任何帮助都将非常有用!

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). 目标是用一个分区重新分区任意的闪存驱动器,整个分区占用整个空间并格式化为FAT32(后来的选项为NTFS和EXFAT)。 This will be done in batch, hopefully with 50+ devices at once, so drive letter access is not an option. 这将分批完成,希望一次使用50台以上的设备,因此无法选择驱动器号。 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. 我可以创建一个分区,但是当我尝试使用IOCTL_DISK_SET_PARTITION_INFO_EX设置格式类型时,它以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. 我可以使用实用程序(例如diskpart)手动对设备进行分区,因此我知道设备支持分区和文件系统类型。 Can anyone help? 有人可以帮忙吗? My full source code is below, it's failing on the DeviceIoControl() call with IOCTL_DISK_SET_PARTITION_INFO_EX. 我的完整源代码如下,使用IOCTL_DISK_SET_PARTITION_INFO_EX在DeviceIoControl()调用上失败。

#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. 我如上所述使用DeviceIoControl()对磁盘进行分区。 Then I use VDS and the IID_IVdsVolumeMF interface to create the file system, but getting there is a bit of work. 然后,我使用VDS和IID_IVdsVolumeMF接口创建文件系统,但是要花一些工夫。 The goal is to partition and format all flash drives (USB sticks) on the system. 目标是对系统上的所有闪存驱动器(USB记忆棒)进行分区和格式化。 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. VDS将通过IID_IVdsVolumeMF接口执行该格式,但是它不会告诉您(至少我还不知道如何)可移动的设备。 But WMI will tell you which devices are removable, but doesn't have a format function. 但是WMI会告诉您哪些设备可移动,但没有格式化功能。 So... 所以...

First use WMI to get a list of all removable volume paths on the system, for example: 首先使用WMI获取系统上所有可移动卷路径的列表,例如:

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. 然后使用VDS获取计算机上所有VDS卷的列表。 Basically you load VDS, then get all software providers. 基本上,您加载VDS,然后获取所有软件提供程序。 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: 现在使用此VDS卷对象格式化卷:

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! 并预先格式化您的USB记忆棒! Whew.. but it seems to be working. ew ..但它似乎正在工作。

Could Microsoft really not have made this any easier? 微软真的不能使这变得容易吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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