The Storage Property ID is a collection of storage information available for each physical device. For example the data includes settings such as Trim Property and Seek Penalty property which can be useful in detecting SSD drives.
The attached program displays the contents returned from the collection of storage information using the DeviceIoControl IOCTL_STORAGE_QUERY_PROPERTY. But it is incomplete because of anomalies with the returned data, and requires testing on a wider range of hardware and OS variants to flush out all the anomalies.
There are 23 different properties although not every device has every property, and some properties are OS dependent, and many properties are for the drivers, and some are reserved for future use. Furthermore, some of the properties are variable length, and others are fixed length.
The Windows DeviceIoControl I/O API is used to access the Storage Properties, using a STORAGE PROPERTY QUERY structure passing the Storage Property ID of the request required.
In scanning this collection, the first step is to determine if the property exists for the specific device & OS combination, then the next step is to determine the length of the property, and finally request the data.
This should be easy, because it is possible to query whether a property exists, and as every property has a common header containing the size of the property, it is also possible to read the header to discover the size.
Unexpected Results
But, from testing, not all properties respond to a query exists, and not all properties respond to a query for the common header, and adding to this, some properties provide wrong size information.
I would like to build a robust Storage Property ID query which discovers the correct data taking into account all these quirks. But because the response varies with each storage device and storage adapter, and varies with each OS, I can only test the few scenarios I have available to me, and would welcome results from different hardware and different OS.
For example I have detected the following errors on 3 computers:
Information on the Storage Property
A brief summary of the ID’s Functions and Structures and the problems already identified, including:
Storage Property ID for the Storage Properties
PropertyExistsQuery to report of the Storage Property exists for the target Hardware/OS combination
Storage Descriptor Header which is common for all Storage Properties
Storage Property ID
The list of Storage Property ID's are described on the MSDN documentation: STORAGE_PROPERTY_ID enumeration
QueryType - Contains flags indicating the type of query to be performed, with two values
Sending the PropertyExistsQuery should report whether the descriptor is supported, but the results from the Windows 10 computer show a number of Property ID's report incorrectly report they don’t exist whereas they do.
It includes in the remarks: An application can determine the required buffer size by issuing a IOCTL_STORAGE_QUERY_PROPERTY control code passing a STORAGE_DESCRIPTOR_HEADER structure for the output buffer, and then using the returned Size member of the STORAGE_DESCRIPTOR_HEADER structure to allocate a buffer of the proper size.
The driver returns query data to the buffer at Irp->AssociatedIrp.SystemBuffer. Varying amounts of bus-specific data can be appended to the structure. Cast the structure returned to a STORAGE_DESCRIPTOR_HEADER and check its Size member to determine the number of bytes the structure actually requires.
From these three references one would expect the Query of the STORAGE_DESCRIPTOR_HEADER to provide the correct Size, but as I have found, this cannot be relied upon. Firstly, with some Storage Property ID's, the header is not returned, and in other cases, the size returned does not contain the correct number of bytes actually required.
I have adapted the application to detect these anomalies, but this is only based on the results from a few computers. There are some properties which are not available on any of the computers I have tested, so I cannot confirm the structure returned. For the moment these return an error message “Under Construction”
Testing Request
I created the attached program to explore the responses to the Storage Property ID queries, and would like feedback on the results for many different devices and OS.
The main component is the module mSTORAGE_PROPERTY.bas which has the function to retrieve data using the DeviceIoControl, and display the results for each of the property structures.
The attached program also includes modules to facilitate the testing and in particular to create an error log file which can be emailed.
Run the attached VB6 program, enter the drive letter, and then select the test button. As well as displaying the results from each property it also displays any errors. The error results can then be emailed so I can collate them and report on the findings and update this application. Not all the properties has been tested and the returned information may include the following notifications:
Reserved – MSDN lists this property as reserved so nothing to return
Driver – MSDN lists this property for drive usage only nothing to return
Under Construction – meaning I have not been able to test this module as none of the computers I have used returns any data for the specific property
Module:
mSTORAGE_PROPERTY.bas
Function:
GetStorageDescriptor
This is a summary, and incomplete code, of key components to retrieve the Storage Property. For the full code, see the attached files.
The guts of the program makes 3 or 4 queries on each of the Storage Property ID’s
Query Exists
Tests if the specific Storage Property ID exists for the drive selected
Query Header
Requests the STORAGE_DESCRIPTOR_HEADER which should be common to all properties
Query Structure Header
Requests the Property structure which is specific to each of the Storage Property ID’s
Query additional data
Requests any additional data
These queries return the Storage Descriptor Header and a byte array of all the returned data
Code – Declarations
Code:
Private Declare Function DeviceIoControl Lib "kernel32" _
(ByVal hDevice As Long, _
ByVal dwIoControlCode As Long, _
ByRef lpInBuffer As Any, _
ByVal nInBufferSize As Long, _
ByRef lpOutBuffer As Any, _
ByVal nOutBufferSize As Long, _
ByRef lpBytesReturned As Long, _
ByRef lpOverlapped As Any) As Long
Private Enum STORAGE_QUERY_TYPE
PropertyStandardQuery = 0 'Instructs the driver to return an appropriate descriptor
PropertyExistsQuery = 1 'Instructs the driver to report whether the descriptor is supported
PropertyMaskQuery = 2 'Not currently supported. Do not use
PropertyQueryMaxDefined = 3 'Specifies the upper limit of the list of query types
End Enum
Private Type STORAGE_DESCRIPTOR_HEADER
Version As Long 'Indicates the size of the STORAGE_DEVICE_DESCRIPTOR structure.
Size As Long 'Specifies the total size of the descriptor in bytes,
'including ID strings which are appended to the structure.
End Type
Private Type STORAGE_PROPERTY_QUERY
PropertyId As Long
QueryType As Long
AdditionalParameters(0 To 1 - 1) As Byte
End Type
Dim SPQ As STORAGE_PROPERTY_QUERY
Dim SDH As STORAGE_DESCRIPTOR_HEADER
Code – Function GetStorageDescriptor Parameters: (StoragePropertyID as long, SDRawBA as STORAGE_DESCRIPTOR_RAW, sDrive As String, _
Optional Size as Long)
Where StoragePropertyID is the ID of the Storage property requested
SDRawBA is a structure containing BA() as a byte array of the data returned
sDrive is the target drive
the Optional Size is the expected size of the Storage property if it is a fixed size
The function first opens the drive and then makes the DirectIOControl queries to request the data.
This is an extract of the DirectIOControl calls for the 4 Queries
'-----------------------------------------
'Request SDH - STORAGE DESCRIPTOR HEADER
'-----------------------------------------
If OK Or (Not OK) Then ' as ExistsQuery is not reliable
SPQ.QueryType = PropertyStandardQuery ' = 0
RetSPQ = DeviceIoControl( _
hDrive, IOCTL_STORAGE_QUERY_PROPERTY, _
SPQ, LenB(SPQ), _
SDH, LenB(SDH), _
RetSize, ByVal 0&)
OK = Not (RetSPQ = False)
'-------------------------------------
'Build SDH - STORAGE DESCRIPTOR HEADER
'(if DeviceIoControl QueryHeader fails)
'-------------------------------------
If (Not OK) And (Size > 0) Then
OK = (Size > 0)
SDH.Size = Size ' used for size of byte array
SDH.Version = Size
End If
End If
3. Code – Query Structure
Code:
' -------------------------------------------
' Header SDH.Size has size of data to be read
' -------------------------------------------
If (SDH.Size > LenB(SDH)) Then
ReDim SDRawBA.ba(0 To SDH.Size - 1)
CopyMemory SDRawBA.ba(0), SDH, LenB(SDH)
SPQ.QueryType = PropertyStandardQuery
SPQ.PropertyId = StoragePropertyID
SPQ.AdditionalParameters(0) = 0
RetSPQ = DeviceIoControl( _
hDrive, IOCTL_STORAGE_QUERY_PROPERTY, _
SPQ, LenB(SPQ), _
SDRawBA.ba(0), SDH.Size, _
RetSize, ByVal 0&)
OK = Not (RetSPQ = False)
End If
4. Code – Query additional Data
Code:
' -------------------------------------------
' Header SDH.Size has size of data to be read
' but there may be more data
' -------------------------------------------
If OK Then ' check RetSize >= SDH.Size
CopyMemory SDH, SDRawBA.ba(0), LenB(SDH)
If SDH.Size > RetSize Then
ReDim SDRawBA.ba(0 To SDH.Size - 1)
CopyMemory SDRawBA.ba(0), SDH, LenB(SDH)
SPQ.QueryType = PropertyStandardQuery ' = 0
RetSPQ = DeviceIoControl( _
hDrive, IOCTL_STORAGE_QUERY_PROPERTY, _
SPQ, LenB(SPQ), _
SDRawBA.ba(0), SDH.Size, _
RetSize3, ByVal 0&)
OK = Not (RetSPQ = False)
End If
End If
Instructions
Either compile the code to an exe file, or run from the VB6 IDE. It is best to run the application in administrator mode as some MSDN documentation for earlier Windows OS versions made this a requirement, particular if Access Denied errors are reported.
Run the program and enter each drive letter with a different hardware, and select the Test Button.
When all the drives has been tested select the EMAIL button to prepare an email of the results, as follows:
The results of a drive test will populate the list boxes
Run the test for all the drives, the press the email button to create an email report.