草庐IT

windows - 启用存储空间时获取物理磁盘的信息

coder 2024-06-05 原文

当启用 Windows 10 存储空间时,我需要获取系统中所有物理磁盘的以下信息。

  • 模型
  • 序列号
  • 固件版本
  • 容量
  • 磁盘索引
  • 磁盘的 Pnp Id(使用 CM_Get_Parent 获取 SCSI Controller 名称)
  • 位置信息(总线编号、目标 ID 和 LUN)

到目前为止我尝试了什么:

  1. 使用 WMI 类 MSFT_PhysicalDisk 虽然这个类给了我适配器号(所以我可以不用磁盘 PNP),但当磁盘连接到不同的 PCI 存储 Controller (例如 Marvell 92xx SATA 6g Controller )时,它提供的位置信息并不完整。

  2. 二手 SetupDiGetClassDevs使用 GUID_DEVINTERFACE_DISK,将句柄传递给 SetupDiGetDeviceInterface并使用 SetupDiGetDeviceInterfaceDetail用于位置信息(总线/目标 ID/LUN)、PNP Id 和设备路径。我可以将设备路径传递给 CreateFile并获取其余信息(类似于 this 方法)。这个问题是它没有给我所有的物理磁盘。省略存储空间池下的磁盘。

  3. 使用与第二种方法类似的方法,但使用 SetupDiEnumDeviceInfo 代替 SetupDiGetDeviceInterface 和 SetupDiGetDeviceInterfaceDetail。和 CM_Get_DevNode_Registry_Property (使用 here 中的磁盘驱动器 Guid)。虽然这为我提供了所有物理磁盘的位置和 PNP id,但我不能在这里使用任何东西(据我所知)来调用 CreateFile 并获取其余详细信息。

启用存储空间后,如何获取每个物理磁盘的上述详细信息?

附带说明一下,如果有一种方法可以使用 CreateFile 和 DeviceIoControl 从磁盘索引中获取磁盘 PNP id ,这对我也很有帮助。

最佳答案

首先我们需要通过调用CM_Get_Device_Interface_ListW枚举系统中的所有磁盘和 CM_Get_Device_Interface_List_SizeWGUID_DEVINTERFACE_DISK

#include <Shlwapi.h>
#include <cfgmgr32.h>
#undef _NTDDSTOR_H_
#include <ntddstor.h>
#include <ntdddisk.h>

static volatile UCHAR guz;

CONFIGRET EnumDisks(PCSTR prefix, PGUID InterfaceClassGuid)
{
    CONFIGRET err;

    PVOID stack = alloca(guz);
    ULONG BufferLen = 0, NeedLen = 256;

    union {
        PVOID buf;
        PWSTR pszDeviceInterface;
    };

    for(;;) 
    {
        if (BufferLen < NeedLen)
        {
            BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
        }

        switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 
            0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
        {
        case CR_BUFFER_SMALL:
            if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, InterfaceClassGuid, 
                0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
            {
        default:
            return err;
            }
            continue;

        case CR_SUCCESS:

            while (*pszDeviceInterface)
            {
                DbgPrint("Interface=[%S]\n", pszDeviceInterface);

                HANDLE hFile = CreateFileW(pszDeviceInterface, FILE_GENERIC_READ, 
                    FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                if (hFile != INVALID_HANDLE_VALUE)
                {
                    GetDiskPropertyByHandle(hFile);
                    CloseHandle(hFile);
                }

                GetPropertyByInterface(prefix, pszDeviceInterface);

                pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
            }

            return CR_SUCCESS;
        }
    }
}

CONFIGRET EnumDisks()
{
    char prefix[256];
    memset(prefix, '\t', sizeof(prefix));
    prefix[sizeof(prefix) - 1] = 0;
    prefix[0] = 0;
    return EnumDisks(prefix + sizeof(prefix) - 1, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK));
}

CM_Get_Device_Interface_ListW返回多个以 NULL 结尾的 Unicode 字符串,每个字符串代表接口(interface)实例的符号链接(symbolic link)名称。

从一方面来说,这个符号链接(symbolic link)名称可以传递给 CreateFileW 以打开磁盘设备。在此之后我们可以将一些 ioctl 设置到磁盘 - 用于获取

  • 磁盘索引
  • 容量
  • 序列号
  • 分区信息

例子:

void GetDiskPropertyByHandle(HANDLE hDisk)
{
    HANDLE hPartition;
    IO_STATUS_BLOCK iosb;
    STORAGE_DEVICE_NUMBER sdn;
    GET_LENGTH_INFORMATION li;

    NTSTATUS status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
        IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn));

    if (0 <= status && sdn.DeviceType == FILE_DEVICE_DISK && !sdn.PartitionNumber)
    {
        DbgPrint("\\Device\\Harddisk%d\n", sdn.DeviceNumber);

        WCHAR sz[64], *c = sz + swprintf(sz, L"\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);

        WCHAR szSize[32];

        if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
            IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &li, sizeof(li))))
        {
            DbgPrint("Length = %S (%I64x)\n", 
                StrFormatByteSizeW(li.Length.QuadPart, szSize, RTL_NUMBER_OF(szSize)), 
                li.Length.QuadPart);
        }

        UNICODE_STRING ObjectName;
        OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };

        PVOID stack = alloca(guz);

        union {
            PVOID buf;
            PDRIVE_LAYOUT_INFORMATION_EX pdli;
            PSTORAGE_DEVICE_DESCRIPTOR psdd;
            PCSTR psz;
        };

        STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery }; 

        ULONG cb = 0, rcb = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x40, PartitionCount = 4;

        do 
        {
            if (cb < rcb)
            {
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            }

            switch (status = (NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb, 
                IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, cb)))
            {
            case STATUS_SUCCESS:
            case STATUS_BUFFER_OVERFLOW:
                if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
                {
                    if (psdd->Size > cb)
                    {
                        rcb = psdd->Size;
                        status = STATUS_BUFFER_OVERFLOW;
                    }
                    else
                    {
                        if (psdd->SerialNumberOffset)
                        {
                            DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
                        }
                    }
                }
                else
                {
                    status = STATUS_INVALID_PARAMETER;
                }
                break;
            }
        } while (status == STATUS_BUFFER_OVERFLOW);

        for (;;)
        {
            if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
            {
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            }

            if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
                IOCTL_DISK_GET_DRIVE_LAYOUT_EX, 0, 0, buf, cb)))
            {
                if (PartitionCount = pdli->PartitionCount)
                {
                    PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;

                    do 
                    {
                        if (!PartitionEntry->PartitionNumber)
                        {
                            continue;
                        }

                        _itow(PartitionEntry->PartitionNumber, c, 10);

                        RtlInitUnicodeString(&ObjectName, sz);

                        DbgPrint("%wZ\nOffset=%S ", &ObjectName, 
                            StrFormatByteSizeW(PartitionEntry->StartingOffset.QuadPart, szSize, RTL_NUMBER_OF(szSize)));

                        DbgPrint("Length=%S\n", 
                            StrFormatByteSizeW(PartitionEntry->PartitionLength.QuadPart, szSize, RTL_NUMBER_OF(szSize)));

                        char PartitionName[256], *szPartitionName;

                        switch (PartitionEntry->PartitionStyle)
                        {
                        case PARTITION_STYLE_MBR:
                            DbgPrint("MBR: type=%x boot=%x", PartitionEntry->Mbr.PartitionType, PartitionEntry->Mbr.BootIndicator);
                            break;
                        case PARTITION_STYLE_GPT:

                            if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_ENTRY_UNUSED_GUID))
                            {
                                szPartitionName = "UNUSED";
                            }
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_SYSTEM_GUID))
                            {
                                szPartitionName = "SYSTEM";
                            }
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RESERVED_GUID))
                            {
                                szPartitionName = "RESERVED";
                            }
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_BASIC_DATA_GUID))
                            {
                                szPartitionName = "DATA";
                            }
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RECOVERY_GUID))
                            {
                                szPartitionName = "RECOVERY";
                            }
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_SNAPSHOT_GUID))
                            {
                                szPartitionName = "SNAPSHOT";
                            }
                            else
                            {
                                sprintf(szPartitionName = PartitionName, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", 
                                    PartitionEntry->Gpt.PartitionType.Data1,
                                    PartitionEntry->Gpt.PartitionType.Data2,
                                    PartitionEntry->Gpt.PartitionType.Data3,
                                    PartitionEntry->Gpt.PartitionType.Data4[0],
                                    PartitionEntry->Gpt.PartitionType.Data4[1],
                                    PartitionEntry->Gpt.PartitionType.Data4[2],
                                    PartitionEntry->Gpt.PartitionType.Data4[3],
                                    PartitionEntry->Gpt.PartitionType.Data4[4],
                                    PartitionEntry->Gpt.PartitionType.Data4[5],
                                    PartitionEntry->Gpt.PartitionType.Data4[6],
                                    PartitionEntry->Gpt.PartitionType.Data4[7]);
                            }
                            DbgPrint("[%s] %I64x \"%S\"", 
                                szPartitionName,
                                PartitionEntry->Gpt.Attributes,
                                PartitionEntry->Gpt.Name);
                            break;
                        }

                        if (0 <= NtOpenFile(&hPartition, FILE_GENERIC_READ, &oa, &iosb,
                            FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
                        {
                            union {
                                BYTE bb[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 32*sizeof(WCHAR) ];
                                FILE_FS_ATTRIBUTE_INFORMATION ffai;
                            };

                            switch (NtQueryVolumeInformationFile(hPartition, &iosb, &ffai, sizeof(bb), FileFsAttributeInformation))
                            {
                            case STATUS_SUCCESS:
                            case STATUS_BUFFER_OVERFLOW:
                                DbgPrint(" \"%.*S\"\n", ffai.FileSystemNameLength >> 1 , ffai.FileSystemName);
                                break;
                            }

                            NtClose(hPartition);
                        }

                    } while (PartitionEntry++, --PartitionCount);
                }
                return ;
            }

            switch (status)
            {
            case STATUS_BUFFER_OVERFLOW:
                PartitionCount = pdli->PartitionCount;
                continue;
            case STATUS_INFO_LENGTH_MISMATCH:
            case STATUS_BUFFER_TOO_SMALL:
                PartitionCount <<= 1;
                continue;
            default:
                return ;
            }
        }
    }
}

从另一个尺寸我们可以得到Device Instance ID通过调用从接口(interface)字符串 CM_Get_Device_Interface_PropertyWDEVPKEY_Device_InstanceId .之后我们调用CM_Locate_DevNodeW用于获取设备实例句柄。

CONFIGRET GetPropertyByInterface(PCSTR prefix, PCWSTR pszDeviceInterface)
{
    ULONG cb = 0, rcb = 256;

    PVOID stack = alloca(guz);
    DEVPROPTYPE PropertyType;

    CONFIGRET status;

    union {
        PVOID pv;
        PWSTR DeviceID;
        PBYTE pb;
    };

    do 
    {
        if (cb < rcb)
        {
            rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
        }

        status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);

        if (status == CR_SUCCESS)
        {
            if (PropertyType == DEVPROP_TYPE_STRING)
            {
                DbgPrint("%sDeviceID = %S\n", prefix, DeviceID);

                DEVINST dnDevInst; 

                if (CR_SUCCESS == (status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL)))
                {
                    GetPropertyByDeviceID(prefix, dnDevInst);
                }
            }
            else
            {
                status = CR_WRONG_TYPE;
            }

            break;
        }

    } while (status == CR_BUFFER_SMALL);

    return status;
}

使用设备实例句柄,我们可以通过CM_Get_DevNode_PropertyW查询许多设备属性像: DEVPKEY_Device_LocationInfo , DEVPKEY_NAME , DEVPKEY_Device_PDOName , DEVPKEY_Device_FirmwareVersion, DEVPKEY_Device_Model, DEVPKEY_Device_DriverVersion和许多其他 - 查看 devpkey.h

中的完整列表

最后我们可以调用CM_Get_Parent并递归查询父设备的所有这些属性,直到我们不丰富堆栈顶部:

#define OPEN_PDO

void GetPropertyByDeviceID(PCSTR prefix, DEVINST dnDevInst)
{
#ifdef OPEN_PDO
    HANDLE hFile;
    IO_STATUS_BLOCK iosb;
    UNICODE_STRING ObjectName;
    OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
#endif

    CONFIGRET status;

    ULONG cb = 0, rcb = 0x80;

    PVOID stack = alloca(guz);

    DEVPROPTYPE PropertyType;

    union {
        PVOID pv;
        PWSTR sz;
        PBYTE pb;
    };

    static struct  
    {
        CONST DEVPROPKEY *PropertyKey;
        PCWSTR PropertyName;
    } PropertyKeys[] = {
        { &DEVPKEY_Device_PDOName, L"PDOName"},
        { &DEVPKEY_Device_Parent, L"Parent"},
        { &DEVPKEY_Device_DriverVersion, L"DriverVersion"},
        { &DEVPKEY_Device_LocationInfo, L"LocationInfo"},
        { &DEVPKEY_Device_FirmwareVersion, L"FirmwareVersion"},
        { &DEVPKEY_Device_Model, L"Model"},
        { &DEVPKEY_NAME, L"NAME"},
        { &DEVPKEY_Device_InstanceId, L"DeviceID"}
    };

    do 
    {
        int n = RTL_NUMBER_OF(PropertyKeys);

        do 
        {
            CONST DEVPROPKEY *PropertyKey = PropertyKeys[--n].PropertyKey;

            do 
            {
                if (cb < rcb)
                {
                    rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
                }

                status = CM_Get_DevNode_PropertyW(dnDevInst, PropertyKey, &PropertyType, pb, &rcb, 0);

                if (status == CR_SUCCESS)
                {
                    if (PropertyType == DEVPROP_TYPE_STRING)
                    {
                        DbgPrint("%s%S=[%S]\n", prefix, PropertyKeys[n].PropertyName, sz);

#ifdef OPEN_PDO

                        if (!n)
                        {
                            // DEVPKEY_Device_PDOName can use in NtOpenFile

                            RtlInitUnicodeString(&ObjectName, sz);

                            if (0 <= NtOpenFile(&hFile, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &oa,
                                &iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
                            {
                                NtClose(hFile);
                            }
                        }
#endif
                    }
                }

            } while (status == CR_BUFFER_SMALL);

        } while (n);

        if (!*--prefix) break;

    } while (CM_Get_Parent(&dnDevInst, dnDevInst, 0) == CR_SUCCESS);
}

也是由 DEVPKEY_Device_PDOName 返回的字符串我们可以在 NtOpenFile 中使用调用打开PDO设备。

关于windows - 启用存储空间时获取物理磁盘的信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52146560/

有关windows - 启用存储空间时获取物理磁盘的信息的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  5. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  6. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  7. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  8. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  9. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  10. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

随机推荐