下文转载自http://blog.51cto.com/cutebunny/625577
有了前几节的基础后,本节给出一个更复杂但却非常实用的例子。
很多情况下,我们想知道当前系统下安装了多少块磁盘,他们的物理驱动器号都是多少,每一块磁盘上有多少个分区,分区号怎么分布,每个分区大小是多少。这就类似于我们打开windows
的磁盘管理看到的那种非常清晰的列表。对于后几个问题,我们根据物理驱动器号调用第五节
http://cutebunny.blog.51cto.com/301216/624567
中的
GetPartitionLetterFromPhysicalDrive
函数,以及第三节
http://cutebunny.blog.51cto.com/301216/624079
中的
GetDiskDriveLayout
函数即可搞定。那么我们这一节的重点放在如何获得当前所有物理驱动器号上。
先引入一个新的概念,设备GUID
,它是同类设备统一并且唯一的标识码。对于磁盘,GUID
为
GUID_DEVINTERFACE_DISK
,具体值为{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
。windows
提供一组API
,可以通过GUID
枚举出所有该类型的设备。先给出几个相关API
的简要介绍
HDEVINFOSetupDiGetClassDevs(IN LPGUID ClassGuid, OPTIONALIN PCTSTR Enumerator, OPTIONALIN HWND hwndParent, OPTIONALIN DWORD Flags) ;
其中,
ClassGuid
填入我们感兴趣的设备GUID
,该函数返回满足查询条件的一组设备的信息集合的句柄,该句柄就是获取设备信息的关键钥匙。
WINSETUPAPI BOOL WINAPISetupDiEnumDeviceInterfaces(IN HDEVINFO DeviceInfoSet,IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONALIN LPGUID InterfaceClassGuid,IN DWORD MemberIndex,OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
该函数枚举
SetupDiGetClassDevs
获得的句柄中包含的所有设备。参数
DeviceInfoSet
填入我们上一步中获得的句柄,
InterfaceClassGuid
仍旧是我们感兴趣的GUID
,
MemberIndex
为设备在集合中的索引,从0
开始计数,最后
DeviceInterfaceData
是输出参数,存储枚举出的设备接口,后续可通过此接口获得详细的设备信息。
注意,参数
DeviceInterfaceData.cbSize
在调用前必须初始化为sizeof(SP_DEVICE_INTERFACE_DATA)
,这是函数的强制要求。
WINSETUPAPI BOOL WINAPISetupDiGetDeviceInterfaceDetail(IN HDEVINFO DeviceInfoSet,IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, OPTIONALIN DWORD DeviceInterfaceDetailDataSize,OUT PDWORD RequiredSize, OPTIONALOUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL);
该函数根据上两步中的句柄和接口获取设备的详细信息数据。参数
DeviceInfoSet
和
DeviceInterfaceData
在上两步中获得。输出参数
DeviceInterfaceDetailData
存储着设备信息数据,这个结构体中的成员
DevicePath
就是我们辛辛苦苦找寻的东西了。用它可以作为设备名调用
CreateFile
函数打开设备,之后的操作,嘿嘿,你懂的…
下面是具体代码
/******************************************************************************* Function: get device path from GUID* input: lpGuid, GUID pointer* output: pszDevicePath, device paths* return: Succeed, the amount of found device paths* Fail, -1******************************************************************************/DWORD GetDevicePath(LPGUID lpGuid, CHAR **pszDevicePath){HDEVINFO hDevInfoSet;SP_DEVICE_INTERFACE_DATA ifdata;PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;DWORD nCount;BOOL result;//get a handle to a device information sethDevInfoSet = SetupDiGetClassDevs(lpGuid, // class GUIDNULL, // EnumeratorNULL, // hwndParentDIGCF_PRESENT | DIGCF_DEVICEINTERFACE // present devices);//fail...if (hDevInfoSet == INVALID_HANDLE_VALUE){fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());return (DWORD)-1;}pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(INTERFACE_DETAIL_SIZE);if (pDetail == NULL){return (DWORD)-1;}pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);nCount = 0;result = TRUE;// device index = 0, 1, 2... test the device interface one by onewhile (result){ifdata.cbSize = sizeof(ifdata);//enumerates the device interfaces that are contained in a device information setresult = SetupDiEnumDeviceInterfaces(hDevInfoSet, // DeviceInfoSetNULL, // DeviceInfoDatalpGuid, // GUIDnCount, // MemberIndex&ifdata // DeviceInterfaceData);if (result){// get details about a device interfaceresult = SetupDiGetDeviceInterfaceDetail(hDevInfoSet, // DeviceInfoSet&ifdata, // DeviceInterfaceDatapDetail, // DeviceInterfaceDetailDataINTERFACE_DETAIL_SIZE, // DeviceInterfaceDetailDataSizeNULL, // RequiredSizeNULL // DeviceInfoData);if (result){// copy the path to output bufferstrcpy(pszDevicePath[nCount], pDetail->DevicePath);//printf("%s\n", pDetail->DevicePath);nCount++;}}}free(pDetail);(void)SetupDiDestroyDeviceInfoList(hDevInfoSet);return nCount;}
执行完毕后,所有满足条件的磁盘设备名称都存储在字符串数组
pszDevicePath
中。有了这个关键的数组,后面就可以为所欲为了。
以下是获得所有物理磁盘号的完整代码
/******************************************************************************* Function: get all present disks' physical number* input: N/A* output: ppDisks, array of disks' physical number* return: Succeed, the amount of present disks* Fail, -1******************************************************************************/DWORD GetAllPresentDisks(DWORD **ppDisks){CHAR *szDevicePath[MAX_DEVICE]; // device pathDWORD nDevice;HANDLE hDevice;STORAGE_DEVICE_NUMBER number;BOOL result;DWORD readed;WORD i, j;for (i = 0; i < MAX_DEVICE; i++){szDevicePath[i] = (CHAR *)malloc(INTERFACE_DETAIL_SIZE);if (NULL == szDevicePath[i]){for (j = 0; j < i; j++){free(szDevicePath[i]);}return (DWORD)-1;}}// get the device pathsnDevice = GetDevicePath(const_cast<LPGUID>(&GUID_DEVINTERFACE_DISK), szDevicePath);if ((DWORD)-1 == nDevice){for (i = 0; i < MAX_DEVICE; i++){free(szDevicePath[i]);}return (DWORD)-1;}*ppDisks = (DWORD *)malloc(sizeof(DWORD) * nDevice);// get the disk's physical number one by onefor (i = 0; i < nDevice; i++){hDevice = CreateFile(szDevicePath[i], // drive to openGENERIC_READ | GENERIC_WRITE, // access to the driveFILE_SHARE_READ | FILE_SHARE_WRITE, //share modeNULL, // default security attributesOPEN_EXISTING, // disposition0, // file attributesNULL // do not copy file attribute);if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive{for (j = 0; j < MAX_DEVICE; j++){free(szDevicePath[j]);}free(*ppDisks);fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());return DWORD(-1);}result = DeviceIoControl(hDevice, // handle to deviceIOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCodeNULL, // lpInBuffer0, // nInBufferSize&number, // output buffersizeof(number), // size of output buffer&readed, // number of bytes returnedNULL // OVERLAPPED structure);if (!result) // fail{fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());for (j = 0; j < MAX_DEVICE; j++){free(szDevicePath[j]);}free(*ppDisks);(void)CloseHandle(hDevice);return (DWORD)-1;}*(*ppDisks + i) = number.DeviceNumber;(void)CloseHandle(hDevice);}for (i = 0; i < MAX_DEVICE; i++){free(szDevicePath[i]);}return nDevice;}
代码说明:
1.
调用函数
GetDevicePath
获得前面所说的磁盘设备名称数组。
2.
对每一个磁盘设备,调用
CreateFile
打开并获得设备句柄。
3.
调用操作码为
IOCTL_STORAGE_GET_DEVICE_NUMBER
的
DeviceIoControl
函数获得磁盘物理驱动器号。
4.
将所有物理磁盘号存入数组返回。
大功告成了。可能有朋友会问,
GetDevicePath
不是已经获得了磁盘路径么,你前面说过,这个路径不是
\\.\PhysicalDriveX
就是
\\.\X
:
,那我们解析一下这个字符串不就可以获得磁盘号或者盘符了么。很可惜,这里的磁盘路径出现了第三种形式,而且是毫无章法的形式。打开函数
GetDevicePath
中的注释行//printf("%s\n", pDetail->DevicePath);
将这种形式的路径打印出来,可以看到类似为
\\?\ide#diskwdc_wd1600aajs-08b4a0___________________01.03a01#5&245a6b6d&0&0.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}\\?\ide#diskwdc_wd1600aajs-08b4a0___________________01.03a01#5&37141c12&0&0.1.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
所以,没办法,我们还是得用
DeviceIoControl
找出磁盘号。