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.
 
  1. typedef enum _MEDIA_TYPE
  2. {
  3. RemovableMedia,
  4. FixedMedia
  5. } 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结构体。
  1. typedef struct _STORAGE_PROPERTY_QUERY {
  2. STORAGE_PROPERTY_ID PropertyId;
  3. STORAGE_QUERY_TYPE QueryType;
  4. UCHAR AdditionalParameters[];
  5. } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;

调用时需设置输入参数中的字段

PropertyId = StorageDeviceProperty;
QueryType = PropertyStandardQuery;
以表明我们要查询一个device descriptor,也就是说,只有指定这种类型,
输出参数才会得到STORAGE_DEVICE_DESCRIPTOR类型数据。
 
  1. typedef struct _STORAGE_DEVICE_DESCRIPTOR {
  2. ULONG Version;
  3. ULONG Size;
  4. UCHAR DeviceType;
  5. UCHAR DeviceTypeModifier;
  6. BOOLEAN RemovableMedia;
  7. BOOLEAN CommandQueueing;
  8. ULONG VendorIdOffset;
  9. ULONG ProductIdOffset;
  10. ULONG ProductRevisionOffset;
  11. ULONG SerialNumberOffset;
  12. STORAGE_BUS_TYPE BusType;
  13. ULONG RawPropertiesLength;
  14. UCHAR RawDeviceProperties[];
  15. } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
  1. typedef enum _STORAGE_BUS_TYPE {
  2. BusTypeUnknown = 0x00,
  3. BusTypeScsi,
  4. BusTypeAtapi,
  5. BusTypeAta,
  6. BusType1394,
  7. BusTypeSsa,
  8. BusTypeFibre,
  9. BusTypeUsb,
  10. BusTypeRAID,
  11. BusTypeiScsi,
  12. BusTypeSas,
  13. BusTypeSata,
  14. BusTypeSd,
  15. BusTypeMmc,
  16. BusTypeMax,
  17. BusTypeMaxReserved = 0x7F
  18. } 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字段中。
  1. typedef struct _STORAGE_DESCRIPTOR_HEADER {
  2. ULONG Version;
  3. ULONG Size;
  4. } STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER;
  1. /******************************************************************************
  2. * Function: get the bus type of an disk
  3. * input: drive name (c:)
  4. * output: bus type
  5. * return: Succeed, 0
  6. * Fail, -1
  7. ******************************************************************************/
  8. DWORD GetDriveTypeByBus( const CHAR *drive, WORD *type )
  9. {
  10. HANDLE hDevice; // handle to the drive to be examined
  11. BOOL result; // results flag
  12. DWORD readed; // discard results
  13.  
  14. STORAGE_DESCRIPTOR_HEADER *pDevDescHeader;
  15. STORAGE_DEVICE_DESCRIPTOR *pDevDesc;
  16. DWORD devDescLength;
  17. STORAGE_PROPERTY_QUERY query;
  18.  
  19. hDevice = CreateFile( drive, // drive to open
  20. GENERIC_READ | GENERIC_WRITE, // access to the drive
  21. FILE_SHARE_READ | FILE_SHARE_WRITE, //share mode
  22. NULL, // default security attributes
  23. OPEN_EXISTING, // disposition
  24. , // file attributes
  25. NULL // do not copy file attribute
  26. );
  27. if ( hDevice == INVALID_HANDLE_VALUE ) // cannot open the drive
  28. {
  29. fprintf( stderr, "CreateFile() Error: %ld\n", GetLastError( ) );
  30. return DWORD( - );
  31. }
  32.  
  33. query.PropertyId = StorageDeviceProperty;
  34. query.QueryType = PropertyStandardQuery;
  35.  
  36. pDevDescHeader = (STORAGE_DESCRIPTOR_HEADER *) malloc(
  37. sizeof(STORAGE_DESCRIPTOR_HEADER) );
  38. if ( NULL == pDevDescHeader )
  39. {
  40. return (DWORD) -;
  41. }
  42.  
  43. result = DeviceIoControl( hDevice, // device to be queried
  44. IOCTL_STORAGE_QUERY_PROPERTY, // operation to perform
  45. &query, sizeof query, // no input buffer
  46. pDevDescHeader, sizeof(STORAGE_DESCRIPTOR_HEADER), // output buffer
  47. &readed, // # bytes returned
  48. NULL ); // synchronous I/O
  49. if ( !result ) //fail
  50. {
  51. fprintf( stderr, "IOCTL_STORAGE_QUERY_PROPERTY Error: %ld\n",
  52. GetLastError( ) );
  53. free( pDevDescHeader );
  54. (void) CloseHandle( hDevice );
  55. return DWORD( - );
  56. }
  57.  
  58. devDescLength = pDevDescHeader->Size;
  59. pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *) malloc( devDescLength );
  60. if ( NULL == pDevDesc )
  61. {
  62. free( pDevDescHeader );
  63. return (DWORD) -;
  64. }
  65.  
  66. result = DeviceIoControl( hDevice, // device to be queried
  67. IOCTL_STORAGE_QUERY_PROPERTY, // operation to perform
  68. &query, sizeof query, // no input buffer
  69. pDevDesc, devDescLength, // output buffer
  70. &readed, // # bytes returned
  71. NULL ); // synchronous I/O
  72. if ( !result ) //fail
  73. {
  74. fprintf( stderr, "IOCTL_STORAGE_QUERY_PROPERTY Error: %ld\n",
  75. GetLastError( ) );
  76. free( pDevDescHeader );
  77. free( pDevDesc );
  78. (void) CloseHandle( hDevice );
  79. return DWORD( - );
  80. }
  81.  
  82. //printf("%d\n", pDevDesc->BusType);
  83. *type = (WORD) pDevDesc->BusType;
  84. free( pDevDescHeader );
  85. free( pDevDesc );
  86.  
  87. (void) CloseHandle( hDevice );
  88. return ;
  89. }

代码说明:

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. BOOL
  2. WINAPI
  3. DeviceIoControl(
  4. _In_ (HANDLE) hDevice, // handle to a partition
  5. _In_ (DWORD) IOCTL_STORAGE_QUERY_PROPERTY, // dwIoControlCode
  6. _In_ (LPVOID) lpInBuffer, // input buffer - STORAGE_PROPERTY_QUERY structure
  7. _In_ (DWORD) nInBufferSize, // size of input buffer
  8. _Out_opt_ (LPVOID) lpOutBuffer, // output buffer - see Remarks
  9. _In_ (DWORD) nOutBufferSize, // size of output buffer
  10. _Out_opt_ (LPDWORD) lpBytesReturned, // number of bytes returned
  11. _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. xcode7 调用相册权限无提示

    1) 打开工程的Info.pilst: 2) 把 Bundle name 和 Bundle display name 的 value值 ,改成跟项目app名一致: 这样系统才能正确地接收到调用请求

  2. Jenkins 集成 Sonar

    Jenkins 与 Sonar 集成:Sonar 是 Jenkins 之外独立运行的一个服务.Jenkins 中安装插件 SonarQube(并配置其 Sonar Server 的 URL / Acc ...

  3. **CI中使用IN查询(where_in)

    注意别漏了$this->db->get(); /** * 匹配用户手机号,返回匹配的用户列表 * @param $column_str 'user_id, user_name, user_ ...

  4. python 判断字符编码

    一般情况下,需要加这个: import sys reload(sys) sys.setdefaultencoding('utf-8') 打开其他文件编码用codecs.open 读 下面的代码读取了文 ...

  5. 【LOJ】#2670. 「NOI2012」随机数生成器

    题解 矩阵乘法,注意需要快速乘 矩阵2*2 a c 0 1 代码 #include <iostream> #include <algorithm> #include <c ...

  6. ubuntu12.04上的mongodb卸载

    如果您需要卸载 mongodb,然后有几种方法来完成这取决于你想实现. 一.卸载只是 mongodb 这将删除只是 mongodb 包本身. 1 sudo apt-get remove mongodb ...

  7. odoo 模型与ORM

    型号属性 在/模型添加activity.py文件 class ActivityEvent(models.Model): _name = 'activity.event' _inherit = 'eve ...

  8. 树莓派与微信公众号对接(python)

    一 内网穿透,让外网可以访问树莓派 二 树莓派对接微信 需要安装webpy和python-lxml git clonegit://github.com/webpy/webpy.git ln -s `p ...

  9. Web服务评估工具Nikto

    Web服务评估工具Nikto   Nikto是一款Perl语言编写的Web服务评估工具.该工具主要侧重于发现网站的默认配置和错误配置.它通过扫描的方式,检测服务器.服务和网站的配置文件,从中找出默认配 ...

  10. dSploitzANTI渗透教程之启动zANTI工具

    dSploitzANTI渗透教程之启动zANTI工具 启动zANTI工具 [示例1-2]下面将介绍启动zANTI工具的方法.具体操作步骤如下所示: (1)在Android设备的应用程序界面,选择并启动 ...