hard disk serial returned from DeviceIoControl and STORAGE_DESCRIPTOR_HEADER is empty or has spaces before actual serial

whydoyou wanttoknow 1 Reputation point
2021-12-29T19:04:05.34+00:00

I could get the serial using WMI in a win32 application but I'm working now in a windows service and having problems with WMI so I tried to use DeviceIoControl for PhysicalDrive0 to get the serial number. I looked at some sites online and cam with this function:

std::string get_hard_disk_serial_string()
    {
        // file is a wrapper for HANDLE and CreateFile.
        file drive_file{ L"\\\\.\\PhysicalDrive0", open_mode::existing, access::read, share_mode::read | share_mode::write };

        STORAGE_PROPERTY_QUERY query{};
        query.PropertyId = StorageDeviceProperty;
        query.QueryType = PropertyStandardQuery;

        auto ioctl_size = [&]() -> uint32_t
        {
            STORAGE_DESCRIPTOR_HEADER header{};
            DWORD bytes_returned = 0;
            BOOL ret = ::DeviceIoControl(drive_file.native_handle().get(), IOCTL_STORAGE_QUERY_PROPERTY, &query,
                sizeof(query), &header, sizeof(header), &bytes_returned, nullptr);
            if (!ret)
                throw std::system_error(GetLastError(), std::system_category());
            return header.Size;
        };

        std::vector<char> out_buff;
        out_buff.resize(ioctl_size());

        [&]
        {
            DWORD bytes_returned = 0;
            BOOL ret = ::DeviceIoControl(drive_file.native_handle().get(), IOCTL_STORAGE_QUERY_PROPERTY, &query,
                sizeof(query), out_buff.data(), static_cast<DWORD>(out_buff.size()), &bytes_returned, nullptr);
            if (!ret)
                throw std::system_error(GetLastError(), std::system_category());
        }();

        STORAGE_DEVICE_DESCRIPTOR* descriptor = reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(out_buff.data());
        if (!descriptor->SerialNumberOffset || descriptor->SerialNumberOffset >= out_buff.size())
        {
            printf("serial offset is 0 or greater than buffer size !\n");
            return "";
        }
        // serial is null terminated ansi string
        std::string_view serial = &out_buff[descriptor->SerialNumberOffset];
        printf("disk serial: %s\n", serial.data()); // prints spaces before serial or an empty serial !
        return std::string{ serial };
    }

The problem is that on one computer running windows 10 the serial contained spaces before the actual serial " SERIAL-HERE" and on another one which runs windows server 2012 R2 the serial was totally empty !

Windows development | Windows API - Win32
Developer technologies | C++
Developer technologies | C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
{count} votes

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.