1、枚举所有硬盘
这里主要借助了setup API,这些API主要是NT4.0之后提供的一些用于操作设备的API。
枚举所有硬盘借助了SetupDiGetClassDevs与SetupDiEnumDeviceInterfaces和 SetupDiGetDeviceInterfaceDetail这三个API。
#pragma once
#ifndef DEVICE_DISK_H
#define DEVICE_DISK_H
#include <Windows.h>
#include <Setupapi.h>
#pragma comment( lib, "setupapi.lib" )
#define DevCount 1024
struct DevInterfaceDetaillArray {
DWORD szCount;
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData_[DevCount];
};int GetALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_);
int FreeALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_);
#endif
#include "DeviceDisk.h"
#include <iostream>
#include <string>
using namespace std;
//函数调用出错一定要记得回收内存,否则会内存泄漏。
//切记要与FreeALLDISK 成对使用。int GetALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_) {
HDEVINFO diskClassDevices;
GUID diskClassDeviceInterfaceGuid = GUID_DEVINTERFACE_DISK;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData;
DWORD requiredSize;
DWORD deviceIndex;
//
// Get the handle to the device information set for installed
// disk class devices. Returns only devices that are currently
// present in the system and have an enabled disk device
// interface.
//
diskClassDevices = SetupDiGetClassDevs(&diskClassDeviceInterfaceGuid,
NULL,
NULL,
DIGCF_PRESENT |
DIGCF_DEVICEINTERFACE);
if (INVALID_HANDLE_VALUE == diskClassDevices) {
return -1;
}
ZeroMemory(&deviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
deviceIndex = 0;while (SetupDiEnumDeviceInterfaces(diskClassDevices,
NULL,
&diskClassDeviceInterfaceGuid,
deviceIndex,
&deviceInterfaceData)) {
SetupDiGetDeviceInterfaceDetail(diskClassDevices,
&deviceInterfaceData,
NULL,
0,
&requiredSize,
NULL);
//回收掉临时内存
if (!(ERROR_INSUFFICIENT_BUFFER == GetLastError())) {
wprintf_s(L"122\n"); //正常情况下,就是获得122错误码,咱也不知道windows在干啥。
if (INVALID_HANDLE_VALUE != diskClassDevices) {
SetupDiDestroyDeviceInfoList(diskClassDevices);
}
return -1;
}
deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
ZeroMemory(deviceInterfaceDetailData, requiredSize);
deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);if (!SetupDiGetDeviceInterfaceDetail(diskClassDevices,
&deviceInterfaceData,
deviceInterfaceDetailData,
requiredSize,
NULL,
NULL)) {
//没有将此次的deviceInterfaceDetailData 内存返回到DevInterfaceDetailArr_中,所以在返回之前必须释放掉,防止内存泄漏。
free(deviceInterfaceDetailData);
//回收掉临时内存
if (INVALID_HANDLE_VALUE != diskClassDevices) {
SetupDiDestroyDeviceInfoList(diskClassDevices);
}
return -1;
}
DevInterfaceDetailArr_->deviceInterfaceDetailData_[deviceIndex] = deviceInterfaceDetailData;
++deviceIndex;
}DWORD Err_Code = GetLastError();
DevInterfaceDetailArr_->szCount = deviceIndex;
//返回之前回收临时内存
if (INVALID_HANDLE_VALUE != diskClassDevices) {
SetupDiDestroyDeviceInfoList(diskClassDevices);
}
if (ERROR_NO_MORE_ITEMS == Err_Code) {
//正常退出while的状态码:259,no more data availible。
return 0;
}
return -2; //0正常执行成功,-1循环内部执行出错,-2执行出错退出循环。
}
int FreeALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_) {
for (int i = 0; i < DevInterfaceDetailArr_->szCount; i++) {
free(DevInterfaceDetailArr_->deviceInterfaceDetailData_[i]);
}
return 0;
}
2、获取硬盘信息(容量、制造商,销售商,版本,硬盘号(即硬盘在系统中显示的序号)等)
核心就是通过createfile打开硬盘设备,获得一个硬盘设备相关的处理句柄。
然后通过DeviceIoControl与对应的控制码来获取硬盘相关信息。
一个使用了这种类似方法的项目地址:GitHub - comefromezero/efiboot: a efiboot tool on windows
下面的代码只是示例,并不能直接运行。
if (0 != GetALLDISK(&DevIn)) {
wprintf_s(L"调用GetALLDISK出错!");
FreeALLDISK(&DevIn);
return -1;
}
HANDLE hdisk = INVALID_HANDLE_VALUE ;
PSTORAGE_DEVICE_DESCRIPTOR pDevDesc;
STORAGE_PROPERTY_QUERY Query;
DWORD dwOutBytes;
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR)new BYTE[sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1];
ZeroMemory(pDevDesc, sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1);
pDevDesc->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1;
//wprintf_s(L"%d", pDevDesc->Size);
//struct EFI_Disk_Info efi_disk[1024] = { 0 };
//g_efi_disk = efi_disk;GET_LENGTH_INFORMATION disk_len = { 0 };
STORAGE_DEVICE_NUMBER diskNumber = { 0 };
DWORD sizePar = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 128;
PDRIVE_LAYOUT_INFORMATION_EX ParTable = (PDRIVE_LAYOUT_INFORMATION_EX)new BYTE[sizePar];
ZeroMemory(ParTable, sizePar);
int devIndex = 0;TCHAR szPathName[MAX_PATH + 1] = { 0 };
for (devIndex = 0; devIndex < DevIn.szCount; devIndex++) {
wprintf_s(L"DiskInfo:%s\n", DevIn.deviceInterfaceDetailData_[devIndex]->DevicePath);
hdisk = CreateFile(DevIn.deviceInterfaceDetailData_[devIndex]->DevicePath,
GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hdisk) {
wprintf_s(L"createfile error! error_code:%d\n", GetLastError());
return -1;
}
if (!DeviceIoControl(hdisk,
IOCTL_STORAGE_QUERY_PROPERTY,
&Query, sizeof(STORAGE_PROPERTY_QUERY),
pDevDesc, pDevDesc->Size,
&dwOutBytes,
NULL)
){
wprintf_s(L"DeviceIoControl Error! line:238 Error_Code:%d %d\n", GetLastError(),dwOutBytes);
}
if (!DeviceIoControl(hdisk, IOCTL_DISK_GET_LENGTH_INFO,
NULL,
0,
&disk_len, sizeof(disk_len), &dwOutBytes, NULL)) {wprintf_s(L"DeviceIoControl Error! line:248 Error_Code:%d %d\n", GetLastError(), dwOutBytes); //122是正常的。
}
if (!DeviceIoControl(hdisk,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL,
0,
&diskNumber,
sizeof(STORAGE_DEVICE_NUMBER),
&dwOutBytes,
NULL)) {wprintf_s(L"DeviceIoControl Error! line:272 Error_Code:%d %d\n", GetLastError(), dwOutBytes);
}
dwOutBytes = 0;
if (!DeviceIoControl(hdisk,
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
NULL,
0,
ParTable,
sizePar,
&dwOutBytes,
NULL)) {wprintf_s(L"DeviceIoControl Error! line:285 Error_Code:%d %d\n", GetLastError(), dwOutBytes);
}
g_efi_disk[devIndex] = (struct EFI_Disk_Info*)new (struct EFI_Disk_Info);
CloseHandle(hdisk);
hdisk = INVALID_HANDLE_VALUE;
//wprintf_s(L"%d %d\n", dwOutBytes, pDevDesc->Size);
char* p_char_string = (char*)pDevDesc;
stringstream sz_buffer;
g_efi_disk[devIndex]->productor = std::string(&p_char_string[pDevDesc->ProductIdOffset]);
g_efi_disk[devIndex]->version = std::string(&p_char_string[pDevDesc->ProductRevisionOffset]);
g_efi_disk[devIndex]->vendor = std::string(&p_char_string[pDevDesc->VendorIdOffset]);
int buffer_disk_len = disk_len.Length.QuadPart / 1024 / 1024 / 1024;
sz_buffer << buffer_disk_len << "GB";
g_efi_disk[devIndex]->length = sz_buffer.str();
g_efi_disk[devIndex]->number = std::to_string(diskNumber.DeviceNumber);
printf_s("Productor:%s\n", g_efi_disk[devIndex]->productor.c_str());
printf_s("Version:%s\n", g_efi_disk[devIndex]->version.c_str());
printf_s("Vendor:%s\n", g_efi_disk[devIndex]->vendor.c_str());
printf_s("Length:%s\n", g_efi_disk[devIndex]->length.c_str());
printf_s("Number:%s\n", g_efi_disk[devIndex]->number.c_str());wprintf_s(L"===================================================================================\n");
wprintf_s(L"ParTable:\n");
wprintf_s(L"ParTableType:%d\n",ParTable->PartitionStyle);
wprintf_s(L"ParCount:%d\n", ParTable->PartitionCount);
wprintf_s(L"Par0:\nParNum:%d\nParName:%s\n", ParTable->PartitionEntry[0].PartitionNumber,ParTable->PartitionEntry[0].Gpt.Name);
wprintf_s(L"PatLength:%dMB\n", ParTable->PartitionEntry[0].PartitionLength.QuadPart / 1024 / 1024);
wprintf_s(L"ParType:{%04x-%02x-%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
ParTable->PartitionEntry[0].Gpt.PartitionType.Data1,
ParTable->PartitionEntry[0].Gpt.PartitionType.Data2,
ParTable->PartitionEntry[0].Gpt.PartitionType.Data3,
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[0],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[1],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[2],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[3],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[4],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[5],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[6],
ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[7]);
wprintf_s(L"ParGUID:{%04x-%02x-%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
ParTable->PartitionEntry[0].Gpt.PartitionId.Data1,
ParTable->PartitionEntry[0].Gpt.PartitionId.Data2,
ParTable->PartitionEntry[0].Gpt.PartitionId.Data3,
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[0],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[1],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[2],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[3],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[4],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[5],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[6],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[7]);
wprintf_s(L"返回的字节数:%d\n", dwOutBytes);
//ParTable = { 0 };
wprintf_s(L"===================================================================================\n");
wsprintf(szPathName, L"\\\\\?\\Volume{%04x-%02x-%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\",
ParTable->PartitionEntry[0].Gpt.PartitionId.Data1,
ParTable->PartitionEntry[0].Gpt.PartitionId.Data2,
ParTable->PartitionEntry[0].Gpt.PartitionId.Data3,
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[0],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[1],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[2],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[3],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[4],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[5],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[6],
ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[7]);
wprintf_s(L"%s\n", szPathName);
}
delete pDevDesc;
FreeALLDISK(&DevIn);
3、获取卷或分区相关信息(卷标名字和文件系统格式等)
核心API就是GetVolumeInfomation来获得对应的信息。
一个使用该办法的项目地址:
GitHub - comefromezero/efiboot: a efiboot tool on windows
以下示例不能直接运行。
if (0 == GetVolumeInformation(ParName, VolumeName, 1024, NULL, NULL, NULL, FileSysName, 1024)) {
wprintf_s(L"Line:%d Err_Code:%d\n",__LINE__, GetLastError());
return -1;
}
4、获取 volume所在硬盘的硬盘号
有了硬盘号就能通过createfile创建一个硬盘设备的句柄,然后利用DeviceIoControl获取硬盘相关信息。
一个使用该办法的项目地址:
GitHub - comefromezero/efiboot: a efiboot tool on windows
以下示例不能直接运行。
//获取分区所在硬盘
TCHAR Buf_Par_Name[1024] = { 0 };
memcpy(Buf_Par_Name, ParName, wstrlen(ParName)-4); //构建分区设备,用于createfile打开分区。
//wprintf_s(L" Line:%d ParName:%s\n",__LINE__, Buf_Par_Name);
HANDLE h_vol = INVALID_HANDLE_VALUE;
h_vol = CreateFile(Buf_Par_Name,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);if (INVALID_HANDLE_VALUE == h_vol) {
wprintf_s(L"Line:%d Err_Code:%d\n",__LINE__, GetLastError());
return -1;
}DWORD dwOutBytes = 0;
PVOLUME_DISK_EXTENTS vol_disk_info = (PVOLUME_DISK_EXTENTS)new BYTE[sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 10];
ZeroMemory(vol_disk_info, sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 10);
if (!DeviceIoControl(h_vol,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL, 0,
vol_disk_info, sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 10,
&dwOutBytes,
NULL)
) {wprintf_s(L"DeviceIoControl Error! line:%d Error_Code:%d %d\n",__LINE__, GetLastError(), dwOutBytes);
}
其中VOLUME_DISK_EXTENTS结构体中就保存了硬盘号。
5、获取一个硬盘中所有的分区
使用DeviceIoControl和控制码IOCTL_DISK_GET_DRIVE_LAYOUT_EX枚举出该硬盘的所有的分区入口项,然后就可以知道该硬盘上所有的分区数量以及其相关信息了。
6、枚举出所有的volume信息
FindFirstVolume与FindNextVolume这两个API可以枚举出所有的分区路径,然后通过GetVolumeInfomation这个API就能够获得对应的信息了。
这里有一个问题,在容量这个问题上,GetDiskFreeSpaceA这个获取的是簇的数量,这就有一个对齐的问题,这个API获得的volume容量可能与分区的实际容量不符,因为分区的时候没有文件系统,但是格式化为某个文件系统之后,文件系统对分区容量进行管理,所以出现GetDiskFreeSpaceA容量可能与分区的实际容量不符的问题,这需要注意一下。
系统中显示的volume容量与GetDiskFreeSpaceA容量一致。