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

下文转载自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

{

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值