您的位置:首页 > 其它

windows的磁盘操作之七——获取当前所有的物理磁盘号

2013-04-24 11:03 351 查看
原创作品,允许转载,转载时请务必以超链接形式标明文章
原始出处 、作者信息和本声明。否则将追究法律责任。http://cutebunny.blog.51cto.com/301216/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的简要介绍

HDEVINFO
SetupDiGetClassDevs(
IN LPGUID ClassGuid, OPTIONAL
IN PCTSTR Enumerator, OPTIONAL
IN HWND hwndParent, OPTIONAL
IN DWORD Flags
);
其中,ClassGuid填入我们感兴趣的设备GUID,该函数返回满足查询条件的一组设备的信息集合的句柄,该句柄就是获取设备信息的关键钥匙。

WINSETUPAPI BOOL WINAPI
SetupDiEnumDeviceInterfaces(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN 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 WINAPI
SetupDiGetDeviceInterfaceDetail(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, OPTIONAL
IN DWORD DeviceInterfaceDetailDataSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT 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 set

hDevInfoSet = SetupDiGetClassDevs(
lpGuid, // class GUID

NULL, // Enumerator
NULL, // hwndParent
DIGCF_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 one
while (result)
{
ifdata.cbSize = sizeof(ifdata);

//enumerates the device interfaces that are contained in a device information set
result = SetupDiEnumDeviceInterfaces(
hDevInfoSet, // DeviceInfoSet
NULL, // DeviceInfoData
lpGuid, // GUID
nCount, // MemberIndex
&ifdata // DeviceInterfaceData
);
if (result)
{
// get details about a device interface
result = SetupDiGetDeviceInterfaceDetail(
hDevInfoSet, // DeviceInfoSet
&ifdata, // DeviceInterfaceData
pDetail, // DeviceInterfaceDetailData
INTERFACE_DETAIL_SIZE, // DeviceInterfaceDetailDataSize
NULL, // RequiredSize
NULL // DeviceInfoData
);
if (result)
{
// copy the path to output buffer
strcpy(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 path
DWORD 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 paths
nDevice = 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 one
for (i = 0; i < nDevice; i++)
{
hDevice = CreateFile(
szDevicePath[i], // drive to open
GENERIC_READ | GENERIC_WRITE, // access to the drive
FILE_SHARE_READ | FILE_SHARE_WRITE, //share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL // 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 device
IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
&number, // output buffer
sizeof(number), // size of output buffer
&readed, // number of bytes returned
NULL // 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找出磁盘号。






本文出自 “bunny技术坊” 博客,请务必保留此出处http://cutebunny.blog.51cto.com/301216/625577
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: