You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

使用IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER失败,如何无注册表获取Windows磁盘序列号?

获取磁盘序列号(无需注册表)的正确方法

你遇到的ERROR_INVALID_FUNCTION问题,本质是IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER这个控制码的兼容性极差——它只适配少数老式存储设备(比如早期IDE硬盘、部分可移动媒体),绝大多数现代硬盘(SSD、NVMe、SATA硬盘等)都不支持这个IOCTL,所以调用必然失败。

下面给你一个微软官方推荐的通用方案,使用IOCTL_STORAGE_QUERY_PROPERTY来获取磁盘序列号,兼容性覆盖几乎所有现代存储设备,且完全不需要读取注册表:

完整代码示例

#include <windows.h>
#include <stdio.h>
#include <initguid.h>
#include <ntddstor.h>

int main() {
    // 打开物理磁盘设备(注意替换PhysicalDrive0为你要查询的磁盘编号)
    HANDLE hDisk = CreateFile(L"\\\\.\\PhysicalDrive0", 
                              GENERIC_READ, 
                              FILE_SHARE_READ | FILE_SHARE_WRITE, 
                              NULL, 
                              OPEN_EXISTING, 
                              FILE_ATTRIBUTE_NORMAL, 
                              NULL);

    if (hDisk == INVALID_HANDLE_VALUE) {
        printf("打开磁盘失败,错误码: %d\n", GetLastError());
        return 1;
    }

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

    // 第一步:查询所需的输出缓冲区大小
    STORAGE_DESCRIPTOR_HEADER header = {0};
    DWORD bytesReturned = 0;
    if (!DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY,
                         &query, sizeof(query),
                         &header, sizeof(header),
                         &bytesReturned, NULL)) {
        printf("获取描述符头部失败,错误码: %d\n", GetLastError());
        CloseHandle(hDisk);
        return 1;
    }

    // 分配足够的缓冲区存储完整设备属性
    DWORD bufferSize = header.Size;
    BYTE* buffer = (BYTE*)malloc(bufferSize);
    if (!buffer) {
        printf("内存分配失败\n");
        CloseHandle(hDisk);
        return 1;
    }

    // 第二步:获取完整的设备属性数据
    if (!DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY,
                         &query, sizeof(query),
                         buffer, bufferSize,
                         &bytesReturned, NULL)) {
        printf("获取设备属性失败,错误码: %d\n", GetLastError());
        free(buffer);
        CloseHandle(hDisk);
        return 1;
    }

    // 解析并输出磁盘序列号
    STORAGE_DEVICE_DESCRIPTOR* deviceDesc = (STORAGE_DEVICE_DESCRIPTOR*)buffer;
    if (deviceDesc->SerialNumberOffset != 0) {
        // 磁盘序列号以UTF-16宽字符格式存储
        WCHAR* serialNumberWide = (WCHAR*)(buffer + deviceDesc->SerialNumberOffset);
        printf("磁盘序列号(宽字符): %ls\n", serialNumberWide);
        
        // 如果需要转换为ANSI格式输出
        char serialNumberAnsi[256];
        WideCharToMultiByte(CP_ACP, 0, serialNumberWide, -1, serialNumberAnsi, sizeof(serialNumberAnsi), NULL, NULL);
        printf("磁盘序列号(ANSI): %s\n", serialNumberAnsi);
    } else {
        printf("该磁盘未提供序列号\n");
    }

    // 清理资源
    free(buffer);
    CloseHandle(hDisk);
    return 0;
}

关键说明

  1. 权限要求:程序必须以管理员权限运行,否则CreateFile会返回错误码5(拒绝访问)。
  2. 磁盘编号PhysicalDrive0对应系统中的第一块物理磁盘,你可以通过磁盘管理工具确认目标磁盘的编号。
  3. 编码处理:磁盘序列号是UTF-16编码,代码中提供了宽字符直接输出和ANSI转换两种方式,可按需选择。
  4. 逻辑磁盘适配:如果要获取C:、D:等逻辑驱动器对应的物理磁盘序列号,需要先通过IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS获取逻辑卷关联的物理磁盘编号,再打开对应的PhysicalDriveN

内容的提问来源于stack exchange,提问作者pts

火山引擎 最新活动