您的位置:首页 > 其它

windows的磁盘操作之九——区分本地磁盘与移动硬盘

2013-04-24 11:04 441 查看
原创作品,允许转载,转载时请务必以超链接形式标明文章
原始出处 、作者信息和本声明。否则将追究法律责任。http://cutebunny.blog.51cto.com/301216/674443

最近碰到了个新问题,记录下来作为windows的磁盘操作那个系列的续篇吧。

一些时候我们的程序需要区分本地存储设备和USB存储设备。在网上搜一搜一般会找到一个最直接的API,GetDriveType,其原型为
UINT GetDriveType(LPCTSTR lpRootPathName)
参数lpRootPathName是存储设备的根目录,例如C:\,返回值即为设备类型。

Return code
Description
DRIVE_REMOVABLE
The drive has removable media; for example, a floppy drive, thumb drive, or flash card reader.
DRIVE_FIXED
The drive has fixed media; for example, a hard drive or flash drive.
或者采用一种稍微复杂一点的方法,使用我们第一节http://cutebunny.blog.51cto.com/301216/624027中介绍的GetDriveGeometry()函数,其输出参数DISK_GEOMETRY
*pdg中的MediaType字段代表设备类型。
typedef enum _MEDIA_TYPE
{

RemovableMedia
FixedMedia

}MEDIA_TYPE;

这两个方法看似能方便快捷的解决我们的需求,但事实上当你使用GetDriveType()去获取一块移动硬盘的类型时,程序会坑爹的告诉你这块移动硬盘的类型是DRIVE_FIXED,根本无法与本地磁盘区分开来。GetDriveGeometry()函数的结果也是如此。
事实上,上述方法只对小容量的U盘有效,会返回给你DRIVE_REMOVABLE的结果;而对移动硬盘甚至是一块稍大容量的U盘(比如我有一块格式化为FAT32格式的4G
U盘),就无能为力了。

所以,我们必须采用别的思路了,这里我介绍一种通过查看总线类型来区分本地磁盘和USB磁盘的方法。当然,其基础还是我们那万能的DeviceIoControl,不过这次的控制码为IOCTL_STORAGE_QUERY_PROPERTY。同时对应的输入参数为STORAGE_PROPERTY_QUERY结构,输出参数为STORAGE_DEVICE_DESCRIPTOR结构体。
typedef struct _STORAGE_PROPERTY_QUERY {

STORAGE_PROPERTY_ID PropertyId;

STORAGE_QUERY_TYPE QueryType;

UCHAR AdditionalParameters[1];

} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
调用时需设置输入参数中的字段
PropertyId = StorageDeviceProperty;
QueryType = PropertyStandardQuery;
以表明我们要查询一个device descriptor,也就是说,只有指定这种类型,输出参数才会得到STORAGE_DEVICE_DESCRIPTOR类型数据。
typedef struct _STORAGE_DEVICE_DESCRIPTOR {

ULONG Version;

ULONG Size;

UCHAR DeviceType;

UCHAR DeviceTypeModifier;

BOOLEAN RemovableMedia;

BOOLEAN CommandQueueing;

ULONG VendorIdOffset;

ULONG ProductIdOffset;

ULONG ProductRevisionOffset;

ULONG SerialNumberOffset;

STORAGE_BUS_TYPE BusType;

ULONG RawPropertiesLength;

UCHAR RawDeviceProperties[1];

} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
其中,BusType定义为
typedef enum _STORAGE_BUS_TYPE {

BusTypeUnknown = 0x00,

BusTypeScsi,

BusTypeAtapi,

BusTypeAta,

BusType1394,

BusTypeSsa,

BusTypeFibre,

BusTypeUsb,

BusTypeRAID,

BusTypeiScsi,

BusTypeSas,

BusTypeSata,

BusTypeSd,

BusTypeMmc,

BusTypeMax,

BusTypeMaxReserved = 0x7F

} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
明白了吧,如果总线类型为BusTypeUsb,就是找到了我们的USB移动硬盘了。
但此时还需要解决一个问题,STORAGE_DEVICE_DESCRIPTOR可以理解为一个变长缓冲区,最后一个字段RawDeviceProperties[1]是可以动态扩展的(windows
API经常有这种情况),那么函数DeviceIoControl()中的参数nOutBufferSize应该填多少呢?这时我们需要借助另一个数据结构STORAGE_DESCRIPTOR_HEADER,在我们不知道device
descriptor实际需要多大的缓冲区时,可以先把STORAGE_DESCRIPTOR_HEADER作为输出参数以获得device descriptor的缓冲区大小,其大小被存入header的size字段中。
typedef struct _STORAGE_DESCRIPTOR_HEADER {

ULONG Version;

ULONG Size;

} STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER;

以下是具体代码
/******************************************************************************
* Function: get the bus type of an disk
* input: drive name (c:)
* output: bus type
* return: Succeed, 0
* Fail, -1
******************************************************************************/
DWORD GetDriveTypeByBus(const CHAR *drive, WORD *type)
{
HANDLE hDevice; // handle to the drive to be examined
BOOL result; // results flag
DWORD readed; // discard results

STORAGE_DESCRIPTOR_HEADER *pDevDescHeader;
STORAGE_DEVICE_DESCRIPTOR *pDevDesc;
DWORD devDescLength;
STORAGE_PROPERTY_QUERY query;

hDevice = CreateFile(
drive, // 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
{
fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());
return DWORD(-1);
}

query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;

pDevDescHeader = (STORAGE_DESCRIPTOR_HEADER *)malloc(sizeof(STORAGE_DESCRIPTOR_HEADER));
if (NULL == pDevDescHeader)
{
return (DWORD)-1;
}

result = DeviceIoControl(
hDevice, // device to be queried
IOCTL_STORAGE_QUERY_PROPERTY, // operation to perform
&query,
sizeof query, // no input buffer
pDevDescHeader,
sizeof(STORAGE_DESCRIPTOR_HEADER), // output buffer
&readed, // # bytes returned
NULL); // synchronous I/O
if (!result) //fail
{
fprintf(stderr, "IOCTL_STORAGE_QUERY_PROPERTY Error: %ld\n", GetLastError());
free(pDevDescHeader);
(void)CloseHandle(hDevice);
return DWORD(-1);
}

devDescLength = pDevDescHeader->Size;
pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *)malloc(devDescLength);
if (NULL == pDevDesc)
{
free(pDevDescHeader);
return (DWORD)-1;
}

result = DeviceIoControl(
hDevice, // device to be queried
IOCTL_STORAGE_QUERY_PROPERTY, // operation to perform
&query,
sizeof query, // no input buffer
pDevDesc,
devDescLength, // output buffer
&readed, // # bytes returned
NULL); // synchronous I/O
if (!result) //fail
{
fprintf(stderr, "IOCTL_STORAGE_QUERY_PROPERTY Error: %ld\n", GetLastError());
free(pDevDescHeader);
free(pDevDesc);
(void)CloseHandle(hDevice);
return DWORD(-1);
}

//printf("%d\n", pDevDesc->BusType);
*type = (WORD)pDevDesc->BusType;
free(pDevDescHeader);
free(pDevDesc);

(void)CloseHandle(hDevice);
return 0;
}

代码说明:
1. 调用CreateFile打开并获得设备句柄。
2. 在输入参数STORAGE_PROPERTY_QUERY query中指定查询类型。
3. 以STORAGE_DESCRIPTOR_HEADER *pDevDescHeader为输出参数,调用操作码为IOCTL_STORAGE_QUERY_PROPERTY的DeviceIoControl函数获得输出缓冲区大小。
4. 按3中获得的缓冲区大小为STORAGE_DEVICE_DESCRIPTOR *pDevDesc分配空间,以pDevDesc为输出参数,调用操作码为IOCTL_STORAGE_QUERY_PROPERTY的DeviceIoControl函数获得device
descriptor。
5. 从device descriptor中获得BusType。

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