赞
踩
简单介绍使用vds.h中的类和方法操作修改硬件/盘符的一些常使用的结构和函数,包括获取格式、删除、创建分区,设置磁盘文件类型,格式化卷等;
值得注意的是即使在vds.h库中像格式化卷这种都有多个方法,需要根据实际需求选择合适的方法。
在对磁盘和卷进行处理时,建议简单了解他们之间的关系,可以参考
【Windows系统】磁盘、Partition和Volume的联系与区别
本文涉及的部分类和函数,优先参考
Qt案例 使用WINDOWS API的VDS.H库查询/修改 WINDOWS系统中硬盘分区/盘符信息(一)
一文中的IVdsDisk 接口和IVdsVolume 接口的声明基础上使用,
注意不要用有重要数据的磁盘/分区来进行测试。容易造成数据丢失!
在部分精简系统或者阉割系统中是不会包含vds服务,需要简单判断是否支持vds服务,以便于后续操作。
bool IsVDSAvailable() { IVdsService* pService = NULL; IVdsServiceLoader* pLoader = NULL; HRESULT hr = CoCreateInstance(CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,IID_IVdsServiceLoader, (void**)&pLoader); if (hr != S_OK) { qDebug("Notice: Disabling VDS (Could not create VDS Loader Instance: %s)", WindowsError::GetVdsError(hr)); goto out; } hr = pLoader->LoadService( L"", &pService); if (hr != S_OK) { qDebug("Notice: Disabling VDS (Could not load VDS Service: %s)", WindowsError::GetVdsError(hr)); goto out; } out: if (pService != NULL) pService->Release(); if (pLoader != NULL) pLoader->Release(); return (hr == S_OK); }
通过VDS_DISK_PROP结构可以获取pwszName,打开磁盘操作对象句柄;
//pwszName : \\?\PhysicalDrive2
HANDLE hDrive = INVALID_HANDLE_VALUE;
hDrive = CreateFileW(prop.pwszName, GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hDrive==INVALID_HANDLE_VALUE)
{
continue;
}
使用 DISK_GEOMETRY_EX 结构 (描述磁盘设备和介质的扩展几何结构)获取磁盘设计大小。
可用获取 DISK_GEOMETRY 结构 获取磁盘的 Cylinders(柱面数)。
磁盘设计大小:柱面数*每个柱面的轨道数*每个轨道的扇区数*每个扇区字节数
DiskSize =Cylinders*TracksPerCylinder*SectorsPerTrack*BytesPerSector
BYTE geometry[256] = {0};
PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry;
DWORD size=0;
if(!DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
NULL, 0, geometry, sizeof(geometry), &size, NULL)||size==0)
{
CloseHandle(hDrive);
continue;
}
//qDebug()<<"柱面数量: "<<QString::number(DiskGeometry->Geometry.Cylinders.QuadPart,10);
使用IVdsAdvancedDisk::FormatPartition方法格式化创建好的分区,此方法仅设置 OEM、ESP 和未知分区的格式,如果是其他分区需要根据磁盘分区偏移量,找到对应的卷然后使用 IVdsVolumeMF::Format 或 IVdsVolumeMF2::FormatEx 方法格式化相应的卷。这样磁盘分区才能正常使用,计算机管理-》磁盘管理才会正常显示分区
/* //!.h VDS 实现此方法。 此方法仅设置 OEM、ESP 和未知分区的格式。 对于其他分区,必须使用 IVdsVolumeMF::Format 或 IVdsVolumeMF2::FormatEx 方法格式化相应的卷。 请注意,OEM、ESP 和未知分区不会作为卷公开,因此不能使用 Format 或 FormatEx 进行格式化。 此方法不能用于格式化可移动媒体。 */ /// The operation is not supported on removable media /// \brief D_FormatPartition 设置现有 OEM、ESP 或未知分区的格式。 /// \param _ullOffset 分区偏移量。 /// \param _type [VDS_FST_NTFS、VDS_FST_FAT、VDS_FST_FAT32或VDS_FST_UDF] /// \param _pwszLabel 表示卷标签的字符串。 /// \param _dwUnitAllocationSize 文件系统分配单元的大小(以字节为单位) /// \param _bForce 如果为 TRUE,则即使正在使用分区,分区也会格式化 /// \param _bQuickFormat 如果为 TRUE,则 VDS 执行快速格式 /// \param _bEnableCompression 如果为TRUE,则对新格式化的文件系统启用压缩 不能为 FAT 和 FAT32 文件系统设置压缩 /// \return /// bool D_FormatPartition( ULONGLONG _ullOffset, VDS_FILE_SYSTEM_TYPE _type, LPWSTR _pwszLabel, DWORD _dwUnitAllocationSize, BOOL _bForce=true, BOOL _bQuickFormat=true, BOOL _bEnableCompression=false); //!.Cpp bool D_FormatPartition( ULONGLONG _ullOffset, VDS_FILE_SYSTEM_TYPE _type, LPWSTR _pwszLabel, DWORD _dwUnitAllocationSize, BOOL _bForce, BOOL _bQuickFormat, BOOL _bEnableCompression) { bool result=false; //pAdvancedDisk 是 IVdsAdvancedDisk 接口的具体实例 IVdsAsync* pAsync=NULL; ULONG ulPercentCompleted=0; HRESULT hResult =pAdvancedDisk->FormatPartition( _ullOffset, _type, _pwszLabel, _dwUnitAllocationSize,_bForce,_bQuickFormat, _bEnableCompression,&pAsync); ULONG jindu=0; while (SUCCEEDED(hResult)) { if (IS_ERROR(hResult)) { pAsync->Cancel(); break; } HRESULT hresult2; hResult = pAsync->QueryStatus( &hresult2, &ulPercentCompleted); if (SUCCEEDED(hResult)) { hResult=hresult2; if (hResult == S_OK) break; if (hResult == VDS_E_OPERATION_PENDING) hResult = S_OK; } if(jindu!=ulPercentCompleted) { jindu=ulPercentCompleted; qDebug()<<" schedule : "<<jindu; } if(ulPercentCompleted==100) break; } result=(hResult==S_OK); if(!result) qDebug()<<"pAdvancedDisk Clean is failed! "<<WindowsError::GetVdsError(hResult); out: return result; }
IOCTL_DISK_SET_DRIVE_LAYOUT_EX 按指定对磁盘进行重新分区。
推荐使用这种方式格式化分区,直接可以同时创建多个分区,唯一需要注意的是
需要简单修改 DRIVE_LAYOUT_INFORMATION_EX 结构体
/* MinGW is unhappy about accessing partitions beside the first unless we redef */
//! MinGW不喜欢访问第一个分区之外的分区,除非我们重新定义
typedef struct _DRIVE_LAYOUT_INFORMATION_EX4 {
DWORD PartitionStyle;
DWORD PartitionCount;
union {
DRIVE_LAYOUT_INFORMATION_MBR Mbr;
DRIVE_LAYOUT_INFORMATION_GPT Gpt;
} Type;
PARTITION_INFORMATION_EX PartitionEntry[16];
} DRIVE_LAYOUT_INFORMATION_EX4, *PDRIVE_LAYOUT_INFORMATION_EX4;
调用:
LONGLONG ullSize=1215277056; ///设置后方分区大小 LONGLONG ullSize=2373588*prop.ulBytesPerSector; LONGLONG ullOffset=DiskUllSize-ullSize; CREATE_DISK CreateDisk = {PARTITION_STYLE_MBR, {{0}}}; CreateDisk.PartitionStyle=PARTITION_STYLE_MBR; CreateDisk.Mbr.Signature=(DWORD)GetTickCount64(); DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0}; DriveLayoutEx.PartitionStyle=PARTITION_STYLE_MBR; DriveLayoutEx.PartitionCount=2; DriveLayoutEx.PartitionEntry[0].PartitionStyle=PARTITION_STYLE_MBR; DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart=ullOffset; DriveLayoutEx.PartitionEntry[0].PartitionLength.QuadPart=ullSize; DriveLayoutEx.PartitionEntry[0].PartitionNumber=1; DriveLayoutEx.PartitionEntry[0].RewritePartition=TRUE; DriveLayoutEx.PartitionEntry[0].Mbr.BootIndicator=TRUE; DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType=0xef; DriveLayoutEx.PartitionEntry[0].Mbr.RecognizedPartition=TRUE; //设置中间间隔 LONGLONG Offset=(2048*prop.ulBytesPerSector); //设置分区大小 LONGLONG OffullSize=DiskUllSize-ullSize-Offset; DriveLayoutEx.PartitionEntry[1].PartitionStyle=PARTITION_STYLE_MBR; DriveLayoutEx.PartitionEntry[1].StartingOffset.QuadPart=Offset; DriveLayoutEx.PartitionEntry[1].PartitionLength.QuadPart=OffullSize; DriveLayoutEx.PartitionEntry[1].PartitionNumber=2; DriveLayoutEx.PartitionEntry[1].RewritePartition=TRUE; DriveLayoutEx.PartitionEntry[1].Mbr.BootIndicator=TRUE; DriveLayoutEx.PartitionEntry[1].Mbr.PartitionType=0xef; DriveLayoutEx.PartitionEntry[1].Mbr.RecognizedPartition=TRUE; DriveLayoutEx.Type.Mbr.Signature = CreateDisk.Mbr.Signature; //hDrive 磁盘对象句柄 DWORD size = sizeof(CreateDisk); //!如果不调用IOCTL_DISK_CREATE_DISK, IOCTL_DISK_SET_DRIVE_LAYOUT_EX调用将失败 bool r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK, (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL); if (!r) { qDebug()<<"Could not reset disk: %s"<< GetError(); return ; } RefreshDriveLayout(); size=sizeof(DriveLayoutEx); r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)&DriveLayoutEx, size, NULL, 0, &size, NULL); if (!r) { qDebug()<<"Could not set drive layout: "<< GetError(); return ; } RefreshDriveLayout();
IVdsCreatePartitionEx 接口 在基本磁盘上创建分区。
IVdsCreatePartitionEx::CreatePartitionEx 在基本磁盘上创建分区。
IVdsCreatePartitionEx 接口实例需要通过IVdsDisk 接口获取。
IVdsCreatePartitionEx* pCreatePartition=NULL;
//判断 IVdsDisk 接口 接口实例是否有效
//IFNULL_GOTO(pDisk,"pDisk No instantiation!",out);
HRESULT hResult= pDisk->QueryInterface(IID_IVdsCreatePartitionEx,(void **)&pCreatePartition);
if (hResult != S_OK)
{
qDebug("Could not initialize_pCreatePartition : %s", GetLastError());
pCreatePartition=NULL;
}
/// 如果起始分区不在第一柱形内,分区后的分区偏移量会发生偏适用于创建一个分区差
/// \brief 在基本磁盘上创建分区。 [此方法取代 IVdsAdvancedDisk::CreatePartition 方法。]
/// *当调用方同时指定 ullOffset 和 ulAlign 参数时,偏移量必须位于第一个柱形内*。
/// \param _ullOffset 分区偏移量
/// \param _ullSize 新分区的大小(以字节为单位)
/// \param _ulAlign 对齐大小(以字节为单位)。
/// \param para gpt/mbr
/// \return
///
bool CreatePartitionEx(ULONGLONG _ullOffset,ULONGLONG _ullSize,ULONG _ulAlign,CREATE_PARTITION_PARAMETERS *para);
bool result=false; //判断IVdsCreatePartitionEx 接口是否有效 //IFNULL_INIT_ISNULL_GOTO(pCreatePartition,initialize_pCreatePartition(),"pCreatePartition No instantiation! ",out); IVdsAsync* pAsync=NULL; ULONG ulPercentCompleted; HRESULT hResult =pCreatePartition->CreatePartitionEx(_ullOffset,_ullSize,_ulAlign,para,&pAsync); ULONG jindu=0; while (SUCCEEDED(hResult)) { if (IS_ERROR(hResult)) { pAsync->Cancel(); break; } HRESULT hresult2; hResult = pAsync->QueryStatus( &hresult2, &ulPercentCompleted); if (SUCCEEDED(hResult)) { hResult=hresult2; if (hResult == S_OK) break; if (hResult == VDS_E_OPERATION_PENDING) hResult = S_OK; } if(jindu!=ulPercentCompleted) { jindu=ulPercentCompleted; qDebug()<<" schedule : "<<jindu; } if(ulPercentCompleted==100) break; } result=(hResult==S_OK); ///if(!result) /// qDebug()<<"pCreatePartition CreatePartitionEx is failed! "<<WindowsError::GetVdsError(hResult);
注意:如果起始分区偏移量不在第一柱形内,分区后的分区偏移量会发生偏移
一般磁盘的一个扇区512字节,一个柱面有255个轨道,每个轨道63个扇区;
所以第一个分区的起始偏移量不能超过 1*255*63*512 字节
发生偏移后,就没办法准确获取对应的卷进行格式化,不推荐使用。
通过FindFirstVolume 扫描计算机的卷。
FindFirstVolume 函数打开卷搜索句柄,并返回有关在计算机上找到的第一个卷的信息。 建立搜索句柄后,可以使用 FindNextVolume 函数搜索其他卷。 如果不再需要搜索句柄,请使用 FindVolumeClose 函数将其关闭。
HANDLE hDrive = INVALID_HANDLE_VALUE, hVolume = INVALID_HANDLE_VALUE; wchar_t * volume_name[MAX_PATH], path[MAX_PATH]; for(uint32_t i=0; true; i++) { if (i == 0) { hVolume = FindFirstVolumeW((LPWSTR)volume_name, sizeof(volume_name)); if (hVolume == INVALID_HANDLE_VALUE) { qDebug("Could not access first GUID volume"); goto out; } } else { if (!FindNextVolumeW(hVolume, (LPWSTR)volume_name, sizeof(volume_name))) { if (GetLastError() != ERROR_NO_MORE_FILES) { qDebug("Could not access next GUID volume"); } qDebug()<<"FindNextVolumeW is failed!"; break; } } QString volume_d=QString::fromWCharArray((LPCWSTR)volume_name); qDebug()<<"[volume_d] "<<volume_d; }
通过 volume_name 打开卷对象句柄
[volume_d] "\\\\?\\Volume{2efb2ac5-6828-4320-8848-041647908928}\\"
QString volume_d=QString::fromWCharArray((LPCWSTR)volume_name);
hDrive = CreateFileW((LPCWSTR)volume_d.mid(0,volume_d.length()-1).toStdWString().c_str(), GENERIC_READ|FILE_READ_ATTRIBUTES|SYNCHRONIZE|FILE_TRAVERSE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hDrive == INVALID_HANDLE_VALUE) {
qDebug("Could not open GUID volume '%s'", volume_name);
continue;
}
VOLUME_DISK_EXTENTS 结构
表示磁盘上的物理位置,可通过此结构获取卷对应的磁盘。经常使用。
///重构结构体
typedef struct _VOLUME_DISK_EXTENTS_OverDide {
DWORD NumberOfDiskExtents;
// Set ANYSIZE_ARRAY = 8
DISK_EXTENT Extents[8];
} VOLUME_DISK_EXTENTS_OverDide;
VOLUME_DISK_EXTENTS_OverDide DiskExtents; DWORD size=0; //hDrive 卷对象句柄 bool r = DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &DiskExtents, sizeof(DiskExtents), &size, NULL); if ((!r) || (size == 0)) { qDebug("Could not get Disk Extents: (empty data)!"); if(hDrive!=NULL) CloseHandle(hDrive); continue; } if (DiskExtents.NumberOfDiskExtents == 0) { qDebug("Ignoring volume '%s' because it has no extents...", volume_name); continue; } if (DiskExtents.NumberOfDiskExtents != 1) { // If we have more than one extent for a volume, it means that someone // is using RAID-1 or something => Stay well away from such a volume! qDebug("Ignoring volume '%s' because it has more than one extent (RAID?)...", volume_name); continue; } // DiskExtents.Extents[0].DiskNumber 磁盘索引 //if (DiskExtents.Extents[0].DiskNumber != Deive_item.DeviceNumber) // Not on our disk // continue;
getVolumePathNamesForVolumeNameW 函数 检索指定卷的驱动器号和装载的文件夹路径的列表。
DWORD CharCount = MAX_PATH + 1;
PWCHAR Names = (PWCHAR) new BYTE [CharCount * sizeof(WCHAR)];
bool Success = GetVolumePathNamesForVolumeNameW((LPCWSTR)volume_name, Names, CharCount, &CharCount);
if ( !Success )
continue;
// qDebug()<<"[Names] : "<<QString::fromWCharArray(Names);
getVolumeInformationByHandleW 函数
检索与指定文件关联的文件系统和卷的相关信息。
LPCWSTR lpRootPathName=(LPCWSTR)Names; DWORD nVolumeNameSize=MAX_PATH+1; LPWSTR lpVolumeNameBuffer=(LPWSTR)new BYTE [nVolumeNameSize * sizeof(WCHAR)]; LPDWORD lpVolumeSerialNumber=0; LPDWORD lpMaximumComponentLength=0; LPDWORD lpFileSystemFlags=0; DWORD nFileSystemNameSize=MAX_PATH+1; LPWSTR lpFileSystemNameBuffer=(LPWSTR)new BYTE [nFileSystemNameSize * sizeof(WCHAR)]; BOOL ishave= GetVolumeInformationByHandleW( hDrive, lpVolumeNameBuffer, nVolumeNameSize, lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags, lpFileSystemNameBuffer, nFileSystemNameSize); if(!ishave) continue; qDebug()<<"[lpVolumeNameBuffer] : "<<QString::fromWCharArray(lpVolumeNameBuffer); qDebug()<<"[lpFileSystemNameBuffer] : "<<QString::fromWCharArray(lpFileSystemNameBuffer);
getDiskFreeSpaceExA 函数 检索有关磁盘卷上可用空间量的信息,即空间总量、可用空间总量以及与调用线程关联的用户可用空间总量。
参数 驱动器根路径,比如“D:”
LPCSTR pszDrive; DWORD64 qwFreeBytesToCaller, qwTotalBytes, qwFreeBytes; DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters; BOOL bResult; //使用GetDiskFreeSpaceEx获取磁盘信息并打印结果 bResult = GetDiskFreeSpaceExA (pszDrive, (PULARGE_INTEGER)&qwFreeBytesToCaller, (PULARGE_INTEGER)&qwTotalBytes, (PULARGE_INTEGER)&qwFreeBytes); if(bResult) { printf("使用GetDiskFreeSpaceEx获取磁盘空间信息\n"); printf("可获得的空闲空间(字节): \t%I64d\n", qwFreeBytesToCaller); printf("空闲空间(字节): \t%I64d\n", qwFreeBytes); printf("磁盘总容量(字节): \t%I64d\n", qwTotalBytes); } //使用GetDiskFreeSpace获取磁盘信息并打印结果 bResult = GetDiskFreeSpaceA (pszDrive, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); if(bResult) { printf("\n使用GetDiskFreeSpace获取磁盘空间信息\n"); printf("空闲的簇数量 : \t%d\n",dwFreeClusters); printf("总簇数量 : \t%d\n",dwTotalClusters); printf("每簇的扇区数量 : \t%d\n",dwSectPerClust); printf("每扇区的容量(字节): \t%d\n",dwBytesPerSect); printf("空闲空间(字节): \t%I64d\n", (DWORD64)dwFreeClusters* (DWORD64)dwSectPerClust*(DWORD64)dwBytesPerSect); printf("磁盘总容量(字节): \t%I64d\n", (DWORD64)dwTotalClusters* (DWORD64)dwSectPerClust*(DWORD64)dwBytesPerSect); }
IVdsVolumeMF3::FormatEx2 方法 格式化分区上的文件系统卷。 此方法与 IVdsVolumeMF2::FormatEx 方法相同,只是使用 Options 参数指定格式设置选项。
IVdsVolumeMF3 * pVolumeMF3=NULL;
//判断 IVdsVolume 接口实例是否有效
//IFNULL_GOTO(pVolume,"pVolume No instantiation!",out);
HRESULT hResult= pVolume->QueryInterface(IID_IVdsVolumeMF3,(void **)&pVolumeMF3);
if (hResult != S_OK)
{
qDebug("Could not initialize_pVolumeMF3 : %s", GetLastError());
pVolumeMF3=NULL;
}
// The following should match VDS_FSOF_FLAGS as much as possible #define FP_FORCE 0x00000001 #define FP_QUICK 0x00000002 LPWSTR _pwszFileSystemTypeName=(LPWSTR)L"NTFS"; USHORT _usFileSystemRevision=NULL; ULONG _ulDesiredUnitAllocationSize=NULL; LPWSTR _pwszLabel=(LPWSTR)L"数据分区" DWORD _Options=FP_FORCE|FP_QUICK bool result=false; //判断pVolumeMF3实例是否有效 //IFNULL_INIT_ISNULL_GOTO(pVolumeMF3,initialize_pVolumeMF3(),"IVdsAdvancedDisk No instantiation! ",out); IVdsAsync* pAsync=NULL; ULONG ulPercentCompleted=0; HRESULT hResult =pVolumeMF3->FormatEx2( _pwszFileSystemTypeName, _usFileSystemRevision, _ulDesiredUnitAllocationSize,_pwszLabel, _Options,&pAsync); ULONG jindu=0; while (SUCCEEDED(hResult)) { if (IS_ERROR(hResult)) { pAsync->Cancel(); break; } HRESULT hresult2; hResult = pAsync->QueryStatus( &hresult2, &ulPercentCompleted); if (SUCCEEDED(hResult)) { hResult=hresult2; if (hResult == S_OK) break; if (hResult == VDS_E_OPERATION_PENDING) hResult = S_OK; } if(jindu!=ulPercentCompleted) { jindu=ulPercentCompleted; qDebug()<<" schedule : "<<jindu; } if(ulPercentCompleted==100) break; } result=(hResult==S_OK); if(!result) qDebug()<<"pVolumeMF3 FormatEx2 is failed! "<<WindowsError::GetVdsError(hResult);
需要注意的是,在格式化分区后都需要根据分区偏移量获取对应的卷,在指定文件格式类型格式化卷。否则创建的卷将不可用,无法识别!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。