0

我一直在尝试使用 C++ 重新分区和格式化 USB 闪存驱动器,任何帮助都会很棒!

目标是使用一个占用整个空间并格式化 FAT32(后来的选项 NTFS 和 EXFAT)的单个分区对任何任意闪存驱动器进行重新分区。这将分批完成,希望一次使用 50 多个设备,因此驱动器号访问不是一个选项。我可以创建一个分区,但是当我尝试 IOCTL_DISK_SET_PARTITION_INFO_EX 设置格式类型时,它失败了,错误为 0x32,ERROR_NOT_SUPPORTED。但目前尚不清楚究竟什么是不支持的。我可以使用 diskpart 等实用程序手动对设备进行分区,因此我知道设备支持的分区和文件系统类型。任何人都可以帮忙吗?我的完整源代码如下,使用 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;
}
4

1 回答 1

1

我有一个解决方案,但它有点令人费解。我使用上面的 DeviceIoControl() 对磁盘进行分区。然后我使用 VDS 和 IID_IVdsVolumeMF 接口来创建文件系统,但是要完成一些工作。目标是对系统上的所有闪存驱动器(USB 记忆棒)进行分区和格式化。VDS 将通过 IID_IVdsVolumeMF 接口执行格式化,但它不会告诉您(至少我还没弄清楚如何)哪些设备是可移动的。但是 WMI 会告诉你哪些设备是可移动的,但没有格式化功能。所以...

首先使用 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)

为您提供路径,例如:

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

然后使用 VDS 获取机器上所有 VDS 卷的列表。基本上你加载 VDS,然后获取所有软件提供商。为简洁起见,此来源缺少部分内容,但我认为我留下的内容足以解释正在发生的事情:

pSvc->QueryProviders(VDS_QUERY_SOFTWARE_PROVIDERS, &pEnumProviders)

现在遍历从每个提供者获取包的提供者列表:

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

现在遍历包并获取每个包中的所有卷:

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

现在您有了可移动设备的路径列表,并且您有了系统上所有卷的列表。是时候比较它们并找出哪些可移动的了。

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! }

现在使用这个 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
}

然后你的 U 盘就被格式化了!唷..但它似乎正在工作。

微软真的不能让这变得更容易吗?

于 2016-09-21T18:43:20.983 回答