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.
 
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[];
} 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[];
} 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 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
, // 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( - );
} query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery; pDevDescHeader = (STORAGE_DESCRIPTOR_HEADER *) malloc(
sizeof(STORAGE_DESCRIPTOR_HEADER) );
if ( NULL == pDevDescHeader )
{
return (DWORD) -;
} 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( - );
} devDescLength = pDevDescHeader->Size;
pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *) malloc( devDescLength );
if ( NULL == pDevDesc )
{
free( pDevDescHeader );
return (DWORD) -;
} 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( - );
} //printf("%d\n", pDevDesc->BusType);
*type = (WORD) pDevDesc->BusType;
free( pDevDescHeader );
free( pDevDesc ); (void) CloseHandle( hDevice );
return ;
}

代码说明:

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。
 
BOOL
WINAPI
DeviceIoControl(
_In_ (HANDLE) hDevice, // handle to a partition
_In_ (DWORD) IOCTL_STORAGE_QUERY_PROPERTY, // dwIoControlCode
_In_ (LPVOID) lpInBuffer, // input buffer - STORAGE_PROPERTY_QUERY structure
_In_ (DWORD) nInBufferSize, // size of input buffer
_Out_opt_ (LPVOID) lpOutBuffer, // output buffer - see Remarks
_In_ (DWORD) nOutBufferSize, // size of output buffer
_Out_opt_ (LPDWORD) lpBytesReturned, // number of bytes returned
_Inout_opt_ (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure

Parameters

hDevice

A handle to the disk device from which partition information is retrieved. To retrieve a device handle, call the CreateFile function.

dwIoControlCode

The control code for the operation. Use IOCTL_STORAGE_QUERY_PROPERTY for this operation.

lpInBuffer

A pointer to a buffer that contains a STORAGE_PROPERTY_QUERY data structure that specifies the details about the query. Device properties must be retrieved only from a device; attempting to retrieve device properties from an adapter will cause an error.

nInBufferSize

The size of the input buffer, in bytes. It must be large enough to contain aSTORAGE_PROPERTY_QUERY data structure.

lpOutBuffer

An optional pointer to a buffer that receives a structure that starts with the same fields as a STORAGE_DESCRIPTOR_HEADER data structure. For more information on the specific structures returned see the Remarks section.

nOutBufferSize

The size of the output buffer, in bytes. It can be zero to determine whether a property exists without retrieving its data. To do that, set this parameter to zero (0) and the QueryType member of the STORAGE_PROPERTY_QUERY input structure to PropertyExistsQuery (1). If the call to DeviceIoControl returns a nonzero value then the property exists.

lpBytesReturned

A pointer to a variable that receives the size of the data stored in the output buffer, in bytes.

If the output buffer is too small, the call fails, GetLastError returnsERROR_INSUFFICIENT_BUFFER, and lpBytesReturned is zero.

If lpOverlapped is NULLlpBytesReturned cannot be NULL. Even when an operation returns no output data and lpOutBuffer is NULLDeviceIoControl makes use oflpBytesReturned. After such an operation, the value of lpBytesReturned is meaningless.

If lpOverlapped is not NULLlpBytesReturned can be NULL. If this parameter is not NULL and the operation returns data, lpBytesReturned is meaningless until the overlapped operation has completed. To retrieve the number of bytes returned, call GetOverlappedResult. If hDevice is associated with an I/O completion port, you can retrieve the number of bytes returned by callingGetQueuedCompletionStatus.

lpOverlapped

A pointer to an OVERLAPPED structure.

If hDevice was opened without specifying FILE_FLAG_OVERLAPPEDlpOverlapped is ignored.

If hDevice was opened with the FILE_FLAG_OVERLAPPED flag, the operation is performed as an overlapped (asynchronous) operation. In this case, lpOverlappedmust point to a valid OVERLAPPED structure that contains a handle to an event object. Otherwise, the function fails in unpredictable ways.

For overlapped operations, DeviceIoControl returns immediately, and the event object is signaled when the operation is complete. Otherwise, the function does not return until the operation is complete or an error occurs.

Return value

If the operation completes successfully, DeviceIoControl returns a nonzero value.

If the operation fails or is pending, DeviceIoControl returns zero. To get extended error information, call GetLastError.

Remarks

The optional output buffer returned through the lpOutBuffer parameter can be one of several structures depending on the value of the PropertyId member of theSTORAGE_PROPERTY_QUERY structure pointed to by the lpInBuffer parameter. These values are enumerated by the STORAGE_PROPERTY_ID enumeration. If the QueryTypemember of the STORAGE_PROPERTY_QUERY is set to PropertyExistsQuery then no structure is returned.

Value lpOutBuffer structure
StorageDeviceProperty (0) STORAGE_DEVICE_DESCRIPTOR
StorageAdapterProperty (1) STORAGE_ADAPTER_DESCRIPTOR
StorageDeviceIdProperty (2) STORAGE_DEVICE_ID_DESCRIPTOR
StorageDeviceUniqueIdProperty (3) STORAGE_DEVICE_UNIQUE_IDENTIFIER
StorageDeviceWriteCacheProperty (4) STORAGE_WRITE_CACHE_PROPERTY
StorageMiniportProperty (5) STORAGE_MINIPORT_DESCRIPTOR
StorageAccessAlignmentProperty (6) STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
StorageDeviceSeekPenaltyProperty (7) DEVICE_SEEK_PENALTY_DESCRIPTOR
StorageDeviceTrimProperty (8) DEVICE_TRIM_DESCRIPTOR
StorageDeviceWriteAggregationProperty(9) DEVICE_WRITE_AGGREGATION_DESCRIPTOR
StorageDeviceLBProvisioningProperty (11) DEVICE_LB_PROVISIONING_DESCRIPTOR
StorageDevicePowerProperty (12) DEVICE_POWER_DESCRIPTOR
StorageDeviceCopyOffloadProperty (13) DEVICE_COPY_OFFLOAD_DESCRIPTOR
StorageDeviceResiliencyProperty (14) STORAGE_DEVICE_RESILIENCY_DESCRIPTOR

windows的磁盘操作之九——区分本地磁盘与移动硬盘的更多相关文章

  1. windows 10 超级优化,同时解决本地磁盘100%的问题

    windows 10 超级优化,同时解决本地磁盘100%的问题 我的系统是笔记本I7处理器,配置了web服务器IIS 和一个数据库(mysql7),同时启用了虚拟机(表中已禁用),以及安装了offic ...

  2. servlet中获取各种相对地址(服务器、服务器所在本地磁盘、src等)。

    [本文简介] 本文将提供javaWeb中经常使用到的相对路径的获取方法,分别有: url基本地址 带目录的url地址 服务器的根路径 服务器所在的 本地磁盘路径 服务器所在的本地磁盘路径,带文件夹 S ...

  3. windows 挂载windows 共享盘为本地磁盘

    我们在设置数据库自动备份时,为了数据的安全往往需要直接将数据备份到远程服务器上.在Linux可以通过NFS挂载来实现,在Windows平台可以直接通过net use+subst来实现将远程服务器的目录 ...

  4. Windows平台将远程服务器的目录挂载为本地磁盘

    我们在设置数据库自动备份时,为了数据的安全往往需要直接将数据备份到远程服务器上.在Linux可以通过NFS挂载来实现,在Windows平台可以直接通过net use+subst来实现将远程服务器的目录 ...

  5. windows的磁盘操作之七——获取当前所有的物理磁盘号 加备注

     windows的磁盘操作之七--获取当前所有的物理磁盘号 2011-07-28 17:47:56 标签:windows API DeviceIoControl 物理磁盘 驱动器号 原创作品,允许转载 ...

  6. Ubuntu SSH 客户端的应用 | sshfs映射远程文件系统为本地磁盘

    SSH是指Secure Shell,是一种安全的传输协议. Ubuntu客户端通过SSH访问远程服务器 ,以下步骤是客户端 的配置方法: 1. sudo apt-get install ssh 2. ...

  7. 实验六:通过grub程序引导本地磁盘内核启动系统(busybox)

    实验名称: 通过grub程序引导本地磁盘内核启动系统(busybox) 实验环境: 理论上,该实验只需要配置好xen环境即可,但是,我们的xen环境安装在centOS7上,但是我们又是使用的kerne ...

  8. VMware虚拟机磁盘操作占用过高问题

    使用虚拟机运行Linux图形桌面时,经常因为一个网页或者编译某个程序就导致虚拟机卡死,甚至影响主机使用.明明主机内存有8G,分配给虚拟机的内存也不少,为什么就这么卡顿.打开主机上的任务管理器查看发现磁 ...

  9. 服务器--远程桌面选择"本地资源"下不显示"本地磁盘"的解决办法(转)

    转自:http://blog.sina.com.cn/s/blog_4cd978f90102wsvc.html “远程连接桌面”,每次连接候,我都选择了“本地资源”下面的“磁盘驱动器”,都会在远程电脑 ...

随机推荐

  1. jenkins主从服务器部署

    当服务器为linux系统但也有部分ios代码,此时就需要添加一个从jenkins以便编译ios代码.或者需要多个job同时编译这时就需要搭建主从服务器. 1.主(master)节点安装jenkins ...

  2. springMVC源码分析--HttpMessageConverter写write操作(三)

    上一篇博客springMVC源码分析--HttpMessageConverter参数read操作中我们已经简单介绍了参数值转换的read操作,接下来我们介绍一下返回值的处理操作.同样返回值的操作操作也 ...

  3. Tango ROS Streamer

    谁想要在Android平台上编写机器人应用,或者谁希望扩展其与室内定位和3D感知新的传感器的机器人开发,Intermodalics创建的ROS Streamer应用的Tango. 这个Android应 ...

  4. 如何用python解析mysqldump文件

    一.前言 最近在做离线数据导入HBase项目,涉及将存储在Mysql中的历史数据通过bulkload的方式导入HBase.由于源数据已经不在DB中,而是以文件形式存储在机器磁盘,此文件是mysqldu ...

  5. Java深度复制List内容。

    最近在工作的时候,有一个小需求,需要复制List的内容,然后会改变其中的数据,但是试了几种复制的方法,都是将原有的数据和复制后的数据都改变了,都没有达到我想要的效果. 其中涉及到了 "浅复制 ...

  6. Datagridview 中的checkbox 选中或勾选状态失效

    1.问题描述,先选中第一行,再取消选择,然后点击部门全选,第一行没有打钩,状态是不选中的状态. 2.分析代码 先选中第一行,单元格的单击事件中 改变选中状态为1,第一行取消选择,单元格的单击事件中 改 ...

  7. CCF CSP 201509-3 模板生成系统

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201509-3 模板生成系统 问题描述 成成最近在搭建一个网站,其中一些页面的部分内容来自数据 ...

  8. USACO 4.3 Buy Low, Buy Lower

    Buy Low, Buy Lower The advice to "buy low" is half the formula to success in the stock mar ...

  9. bzoj 1233

    先要了解一个结论,在多种可行的堆叠方案中,至少有一种能使层数最高的方案同时使得底边最短.即底边最短的,层数一定最高. dp[ i ] = min(sum[j - 1] - sum[i - 1])  j ...

  10. 单元测试+内存、SD卡、SP读写+XmlPullParser

    测试: 测试的相关概念 1.根据是否知道源代码分类: 黑盒测试: a - b - c 边值测试 测试逻辑业务 白盒测试: 根据源代码写测试方法 或者 测试用例; 2.根据测试的粒度分类: 方法测试:写 ...