下文转载自http://blog.51cto.com/cutebunny/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{…RemovableMediaFixedMedia…}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
类型数据。
其中, BusType 定义为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;
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 examinedBOOL result; // results flagDWORD readed; // discard resultsSTORAGE_DESCRIPTOR_HEADER *pDevDescHeader;STORAGE_DEVICE_DESCRIPTOR *pDevDesc;DWORD devDescLength;STORAGE_PROPERTY_QUERY query;hDevice = CreateFile(drive, // 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{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 queriedIOCTL_STORAGE_QUERY_PROPERTY, // operation to perform&query,sizeof query, // no input bufferpDevDescHeader,sizeof(STORAGE_DESCRIPTOR_HEADER), // output buffer&readed, // # bytes returnedNULL); // synchronous I/Oif (!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 queriedIOCTL_STORAGE_QUERY_PROPERTY, // operation to perform&query,sizeof query, // no input bufferpDevDesc,devDescLength, // output buffer&readed, // # bytes returnedNULL); // synchronous I/Oif (!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
。