windows的磁盤操做之九——區分本地磁盤與移動硬盤

http://cutebunny.blog.51cto.com/301216/674443windows

最近碰到了個新問題,記錄下來做爲windows的磁盤操做那個系列的續篇吧。api

一些時候咱們的程序須要區分本地存儲設備和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[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;

調用時需設置輸入參數中的字段app

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;
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移動硬盤了。less

但此時還須要解決一個問題, 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;
}

代碼說明:async

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.this

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.spa

nInBufferSize

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

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
相關文章
相關標籤/搜索