# General > Application Testing >  VB6 DeviceIoControl  IOCTL_STORAGE_QUERY_PROPERTY testing

## powys6

*Name of Application:*
TestSPID*Description of what the Application does:*
Test Storage Property ID functions*Minimum Requirements to test the Application:*
VB6, Any NT based OS including Win10*Your Expectations from the testing:*
Identify unusual Hardware and OS properties
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 PropertiesPropertyExistsQuery to report of the Storage Property exists for the target Hardware/OS combinationStorage 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

*PropertyExistsQuery*
From the MSDN documention:
STORAGE_PROPERTY_QUERY structure

The query consists of two parameters
The Storage Property IDQueryType - 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.

*Storage Descriptor Header*
In the MSDN documentation for the first Storage Property ID, the STORAGE_DEVICE_DESCRIPTOR structure

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.
_
and the MSDN documentation for the STORAGE_DESCRIPTOR_HEADER structure
Describes the contents of the STORAGE DESCRIPTOR HEADER content as



And also from the MSDN documentation
IOCTL_STORAGE_QUERY_PROPERTY control code

Under Output Parameters, 

_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 returnDriver  MSDN lists this property for drive usage only nothing to returnUnder 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 selectedQuery Header 
Requests the STORAGE_DESCRIPTOR_HEADER which should be common to all propertiesQuery Structure Header 
Requests the Property structure which is specific to each of the Storage Property IDsQuery additional data 
Requests any additional data

These queries return the Storage Descriptor Header and a byte array of all the returned data

*Code  Declarations* 



```
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

*1. Code  Query Exists*



```
'---------------------------------------
'Populate STORAGE_PROPERTY_QUERY request
'---------------------------------------
        SPQ.PropertyId = StoragePropertyID
        SPQ.AdditionalParameters(0) = 0

'--------------------------------------------------
'Check if StoragePropertyID is supported
'--------------------------------------------------
        SPQ.QueryType = PropertyExistsQuery ' = 1

        RetSPQ = DeviceIoControl( _
            hDrive, IOCTL_STORAGE_QUERY_PROPERTY, _
            SPQ, LenB(SPQ), _
            ByVal 0&, ByVal 0&, _
            RetSize, ByVal 0&)

        OK = (RetSPQ <> 0)
```

*2 Code  Query Header*



```
'-----------------------------------------
'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*



```
' -------------------------------------------
' 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*



```
' -------------------------------------------
' 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.

Thanks

----------

