如何获取 Windows 磁盘的序列号?

pts*_*pts 1 winapi ioctl windows-7

我正在尝试使用 IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER 获取磁盘的序列号:

HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                        OPEN_EXISTING,
                        FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
  struct {
    USHORT Reserved;
    USHORT SerialNumberLength;
    UCHAR SerialNumber[252];
  } dsn;
  DWORD nr;   
  memset(&dsn, '\0', sizeof dsn);
  if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
                       NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
    printf("Serial number: %s\n", dsn.SerialNumber);
  } else {
    printf("No serial number, error %d.\n", (int)GetLastError());
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,GetLastError()返回 ERROR_INVALID_FUNCTION。

磁盘确实存在,并且它有一个序列号,请参阅此注册表项:

regedit 中带有序列号的磁盘

如何在不使用注册表的情况下从 C 代码中检索序列号?

RbM*_*bMm 5

我们可以使用IOCTL_STORAGE_QUERY_PROPERTYwith StorageDeviceProperty(表示调用者正在查询设备描述符, STORAGE_DEVICE_DESCRIPTOR)

并使用SerialNumberOffset成员STORAGE_DEVICE_DESCRIPTOR

指定从结构开头到包含设备序列号的以 NULL 结尾的 ASCII 字符串的字节偏移量。如果设备没有序列号,则该成员为零。

代码可以是这样的:

ULONG GetSerial(HANDLE hFile)
{
    static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery }; 

    union {
        PVOID buf;
        PSTR psz;
        PSTORAGE_DEVICE_DESCRIPTOR psdd;
    };

    ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;

    ULONG dwError;

    do 
    {
        dwError = ERROR_NO_SYSTEM_RESOURCES;

        if (buf = LocalAlloc(0, size))
        {
            ULONG BytesReturned;

            if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
            {
                if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
                {
                    if (psdd->Size > size)
                    {
                        size = psdd->Size;
                        dwError = ERROR_MORE_DATA;
                    }
                    else
                    {
                        if (psdd->SerialNumberOffset)
                        {
                            DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
                            dwError = NOERROR;
                        }
                        else
                        {
                            dwError = ERROR_NO_DATA;
                        }
                    }
                }
                else
                {
                    dwError = ERROR_GEN_FAILURE;
                }
            }
            else
            {
                dwError = GetLastError();
            }

            LocalFree(buf);
        }
    } while (dwError == ERROR_MORE_DATA);

    return dwError;
}
Run Code Online (Sandbox Code Playgroud)

同样对于我们可以使用的开放设备CreateFileW (L"\\\\.\\PhysicalDrive0", 0, 0, 0, OPEN_EXISTING, 0, 0);- 在适当的地方dwDesiredAccess我们可以使用 0 因为IOCTL_STORAGE_QUERY_PROPERTY定义为 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)- 所以FILE_ANY_ACCESS- 接受任何文件访问并且FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING只对文件系统设备有意义(更一般的使用缓存) - 对于磁盘设备 - 这无关紧要