Results 1 to 1 of 1

Thread: VB6 DeviceIoControl – IOCTL_STORAGE_QUERY_PROPERTY– testing

  1. #1

    Thread Starter
    New Member
    Join Date
    Nov 2015
    Posts
    2

    VB6 DeviceIoControl – IOCTL_STORAGE_QUERY_PROPERTY– testing

    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:

    Name:  Table1-2-3.png
Views: 1723
Size:  30.7 KB

    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

    PropertyExistsQuery
    From the MSDN documention:
    STORAGE_PROPERTY_QUERY structure

    The query consists of two parameters
    1. The Storage Property ID
    2. QueryType - Contains flags indicating the type of query to be performed, with two values


    Name:  Table4.png
Views: 1577
Size:  8.9 KB

    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

    Name:  Table5.png
Views: 1696
Size:  10.1 KB

    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 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
    1. Query Exists
      Tests if the specific Storage Property ID exists for the drive selected
    2. Query Header
      Requests the STORAGE_DESCRIPTOR_HEADER which should be common to all properties
    3. Query Structure Header
      Requests the Property structure which is specific to each of the Storage Property ID’s
    4. 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

    1. Code – Query Exists

    Code:
    '---------------------------------------
    '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

    Code:
    '-----------------------------------------
    '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:

    Name:  Scrren1-2.png
Views: 1685
Size:  156.0 KB

    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
    Attached Files Attached Files

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width