Results 1 to 4 of 4

Thread: What's wrong with DeviceIoControl?

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Oct 2008
    Posts
    1,180

    What's wrong with DeviceIoControl?

    I'm trying to do this in Visual Basic 6. I'm following, to the best of my understanding, how I'm supposed to use the DeviceIoControl API function, and I'm trying to use it with the IOCTL_STORAGE_QUERY_PROPERTY to retrieve info about my harddrive (this includes things like the serial number and the manufacturer ID and product ID). But it keeps giving me error 24, which is ERROR_BAD_LENGTH, according to the Microsoft documentation. I don't know what's wrong.

  2. #2
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,547

    Re: What's wrong with DeviceIoControl?

    The UDTs involved use a lot of unions, bitfields, and c-style arrays, none of which VB supports, so it's very tricky to declare them in a compatible way. And of course the last one requires querying for the required size first.

    I know there's several examples of that particular ioctl though, so just search the forum.

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Oct 2008
    Posts
    1,180

    Re: What's wrong with DeviceIoControl?

    Quote Originally Posted by fafalone View Post
    The UDTs involved use a lot of unions, bitfields, and c-style arrays, none of which VB supports, so it's very tricky to declare them in a compatible way. And of course the last one requires querying for the required size first.

    I know there's several examples of that particular ioctl though, so just search the forum.
    After looking at some other example, I finally figured out how to do it (no thanks to the MS docs, as I'd already read all the applicable docs, but they weren't helping me get past this error). Fortunately I didn't have to deal with bitfields or unions or c-style arrays, for this particular IOCTL. Only thing that I had to account for was the pointers to null terminated strings (which VB6 doesn't support directly, but I was able to write my own function to handle these, using the GetMem1 function of MSVBVM60.DLL to copy the string from a known memory address, one ascii character at a time, until it hit a 0x00 byte). It now does work.

    Here's a screenshot of what I was able to get it to print.
    Name:  gY9BP9P.png
Views: 410
Size:  5.7 KB
    Note that I redacted most of the serial number, because I've heard that unique numbers like this can be useful to track people (after all, that number, along with other info, is commonly used in software activation, to lock your software to just one computer, or in online game bans to ban your computer, regardless if you're using a VPN to dodge an IP ban).

    Here's the final code I got for this.
    Code:
    Private Declare Sub GetMem1 Lib "msvbvm60.dll" (ByRef Source As Any, ByRef Destination As Any)
    Private Declare Function DeviceIoControl Lib "kernel32.dll" (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 Declare Function CreateFile Lib "kernel32.dll" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByRef lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
    
    Private Const FILE_SHARE_READ As Long = 1
    Private Const FILE_SHARE_WRITE As Long = 2
    
    Private Const OPEN_EXISTING As Long = 3
    
    Private Const IOCTL_STORAGE_QUERY_PROPERTY As Long = &H2D1400
    
    Private Type STORAGE_PROPERTY_QUERY
        PropertyID As Long
        QueryType As Long
        AdditionalParameters(4095) As Byte
    End Type
        
    Private Type STORAGE_DEVICE_DESCRIPTOR
        Version As Long
        Size As Long
        DeviceType As Byte
        DeviceTypeModifier As Byte
        RemovableMedia As Byte
        CommandQueueing As Byte
        VendorIdOffset As Long
        ProductIdOffset As Long
        ProductRevisionOffset As Long
        SerialNumberOffset As Long
        BusType As Long
        RawDeviceProperties(4095) As Byte
    End Type
    
    Private Sub Form_Load()
        Dim Query As STORAGE_PROPERTY_QUERY
        Dim Descriptor As STORAGE_DEVICE_DESCRIPTOR
        Dim hDev As Long
        Dim RetVal As Long
        Dim BytesRead As Long
        
        hDev = CreateFile("\\.\PhysicalDrive0", 0, FILE_SHARE_READ + FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0, 0)
            If hDev = -1 Then
                Print Err.LastDllError
                Exit Sub
            End If
            
            RetVal = DeviceIoControl(hDev, IOCTL_STORAGE_QUERY_PROPERTY, Query, LenB(Query), Descriptor, LenB(Descriptor), BytesRead, ByVal 0&)
            If RetVal = 0 Then
                Print Err.LastDllError
                CloseHandle hDev
                Exit Sub
            End If
            
        CloseHandle hDev
        
        With Descriptor
            Print "Vendor ID: ";
            If .VendorIdOffset > 0 Then PrintStringAtAddr .VendorIdOffset + VarPtr(Descriptor) Else Print ""
            Print "Product ID: ";
            If .ProductIdOffset > 0 Then PrintStringAtAddr .ProductIdOffset + VarPtr(Descriptor) Else Print ""
            Print "Product Revision: ";
            If .ProductRevisionOffset > 0 Then PrintStringAtAddr .ProductRevisionOffset + VarPtr(Descriptor) Else Print ""
            Print "Serial Number: ";
            If .SerialNumberOffset > 0 Then PrintStringAtAddr .SerialNumberOffset + VarPtr(Descriptor) Else Print ""
        End With
        
        
    End Sub
    
    
    
    
    Private Sub PrintStringAtAddr(ByVal MemAddr As Long)
        Dim Char As Byte
        Dim Buffer As String
        
        Do
            GetMem1 ByVal MemAddr, Char
            If Char = 0 Then Exit Do
            Buffer = Buffer & Chr$(Char)
            MemAddr = MemAddr + 1
        Loop
        
        Print Trim$(Buffer)
        
    End Sub
    One interesting thing I noticed is that with my harddrive, the Vendor ID field is unused. Instead, the company put their name (along with the model number of the harddrive) in the Product ID field.
    Last edited by Ben321; Dec 4th, 2023 at 11:53 PM.

  4. #4
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,547

    Re: What's wrong with DeviceIoControl?

    Yeah a lot of this stuff is vendor-specific.

    That will get HDD serial numbers. The issue of serial numbers for NVME drives was never resolved unfortunately. I can obtain the NVME_IDENTIFY_CONTROLLER_DATA (same message, but larger buffer needed), but the SN (serial number) field doesn't match, with or without byte order reversal. One of these days I'll buckle down and spend the many hours it would take to follow the path CrystalDiskInfo uses... they've got a huge number of different combinations for different vendors and different drive types.

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